[erlang-questions] ** exception error: no function clause matching test_calculate:validate(["1", "+", "1"], []) (test_calculate.erl, line 11)
Richard A. O'Keefe
ok@REDACTED
Thu Oct 1 05:42:58 CEST 2015
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.
More information about the erlang-questions
mailing list