[erlang-questions] ** exception error: no function clause matching test_calculate:validate(["1", "+", "1"], []) (test_calculate.erl, line 11)

Roelof Wobben r.wobben@REDACTED
Thu Oct 1 08:15:21 CEST 2015


Op 1-10-2015 om 05:42 schreef Richard A. O'Keefe:
> On 1/10/2015, at 4:42 am, Roelof Wobben <r.wobben@REDACTED> wrote:
>> -module(test_calculate).
>>
>> -export([validate/1]).
>>
>> scan(String) ->
>>     validate(string:tokens(String, " ")).
> I'm going to use an entirely informal type notation here.
> I am doing this to emphasise that you not only do not need
> to understand a language's type checker, there doesn't even
> need to BE a type checker for the language for YOU to check
> that the types make sense.
>
> string:tokens(Source :: <string>, Separator :: <string>) :: <string> list.
>
> You can verify this by calling string:tokens/2 in the Erlang shell.
>
> 1> string:tokens("1 + 1", " ").
> ["1","+","1"]
>
> You pass the result of string:tokens/2 to validate/1, so we must have
>
> scan(Source :: <string>) :: SOMETHING.
> validate(Tokens :: <string> list) :: SOMETHING.
>
> That is, scan/1 returns whatever validate/1 returns,
> and validate/1 is given a LIST OF STRINGS as its argument.
>
>> validate(String) ->
>>     validate(String, []).
> Right here alarm bells go off.  We know from the call
> that the argument is not a string.  It is a LIST of strings.
> But the argument *NAME* says 'String'.
>
> validate([Head | Tail], Validated_list) when Head >= $0 , Head =< $9 ->
>>     validate(Tail, [Head | Validated_list]);
> Here you are comparing an element of validate's argument
> as if you are expecting a character.  But Head is a STRING.
>
> It is also a concern to me that you do not have any comment
> saying what it *means* for a list of strings to be valid.
>
> 2> c(test_calculate).
> test_calculate.erl:4: Warning: function scan/1 is unused
> test_calculate.erl:19: Warning: function test/0 is unused
> {ok,test_calculate}
>
> It looks as though you forgot to -export([test/0]).
>> validate([Head | Tail], Validated_list) when Head =:= $+ ->
>>     validate(Tail, [Head | Validated_list]);
>>
>> validate([], Validated_list) ->
>>     Validated_list.
>>
>> test() ->
>>     ["1","+","1"]    = scan("1 + 1"),
>>     ["10", "+", "1"] = scan("10 + 1"),
>>     ["1", "+", "10"] = scan("1 + 10"),
>>     io:format("All tests are green ~n").
>>
>> but as soon as I try :
>>
>> test_calculate:validate(["1","+", "1"])
>>
>> I see the above error
>>
>> Someone a tip where things are going the wrong way ?
> The error message is pretty explicit.
> You have a call
>      validate(["1","+","1"], []).
> No clause matches that call.
>
> You expected that some clause *would* match it.
> So what you do is compare each clause in turn with
> the call to see why *that* clause did not match.
>
> The first clause looks for a CHARACTER between
> $0 and $9 but it finds the STRING "1".
> 4> {"1" >= $0, "1" =< $9}.
> {true,false}
>
> So that clause does not match.
>
> The second clause looks for the CHARACTER $+
> but it finds the STRING "1".
>
> So that clause doesn't match.
>
> The last clause looks for the empty list,
> but it finds a list of three strings.
>
> So that clause doesn't match.
>
> And we've found out what looking at scan/1 and
> validate/1 told us straight away, that you are
> muddling up strings and characters.
>
> So let's fix that.
>
> -module(test_calculate).
> -export([test/0, validate/1]).
>
> scan(String) ->
>      Tokens = string:tokens(String, " "),
>      case validate(Tokens)
>        of ok  -> {ok,Tokens}
>         ; Err -> Err
>      end.
>
> %% A Token_List is valid if and only if it contains
> %% number tokens and operator tokens, where a number
> %% token is a non-empty string of digits and currently
> %% an operator token is just "+".  We either report
> %% 'ok' for a valid list or {error,Reason,Culprit} for invalid.
>
> validate([]) ->
>      ok;
> validate(["+"|Tokens]) ->
>      validate(Tokens);
> validate([Num|Tokens]) ->
>      case is_number_token(Num, 0)
>        of true  -> validate(Tokens)
>         ; false -> {error,"not a number or operator",Num}
>      end;
> validate(Other) ->
>      {error,"not a list",Other}.
>
> %% is_number_token(String, N) is true if and only if
> %% String is a list of ASCII decimal digit characters
> %% and N + length(String) > 0.
>
> is_number_token([], N) ->
>      N > 0;
> is_number_token([C|Cs], N)
>    when C =< $9, C >= $0 ->
>      is_number_token(Cs, N+1);
> is_number_token(_, _) ->
>      false.
>
> test() ->
>     {ok,["1","+","1"]   } = scan("1 + 1"),
>     {ok,["10", "+", "1"]} = scan("10 + 1"),
>     {ok,["1", "+", "10"]} = scan("1 + 10"),
>     io:format("All tests are green ~n").
>
> The single most important step in getting that right was
> WRITING THE COMMENTS.
>
>
>
>
> -----
> Geen virus gevonden in dit bericht.
> Gecontroleerd door AVG - www.avg.com
> Versie: 2015.0.6140 / Virusdatabase: 4419/10729 - datum van uitgifte: 09/30/15
>
>


Thanks for the lesson.
One thing is not clear to me. What is the meaning of N. I see that N is 
equal to zero or to one.

Roelof




More information about the erlang-questions mailing list