Hi guys I have the makings of a parser for a Theorem Prover. I have a module which previously tokenises the string so im inputting: [{bracket,open},{prop,a},{logicOp,'and'},{prop,b},{bracket,close}] to a parser which has an calls an inner function. here is the code:
parse([])-> [];
parse(FullList) ->
parseClauses(FullList,[],[]).
parseClauses([{bracket, open}| RestOfList], StackList, ParsedList) ->
parseClauses(RestOfList,
StackList ++ [{bracket, open}],
ParsedList);
parseClauses([{prop, Any},{logicOp, Any}| RestOfList], StackList, ParsedList) ->
parseClauses(RestOfList,
StackList ++ [{logicOp, Any},{prop, Any}],
ParsedList);
parseClauses([{bracket, close}, {logicOp, Any}| RestOfList],StackList,ParsedList) ->
parseClauses(RestOfList,
StackList ++ [{bracket, close}],
[{logicOp, Any}] ++ ParsedList);
parseClauses([{bracket, close}|RestOfList], StackList, ParsedList) ->
parseClauses(RestOfList,
StackList++[{bracket, close}],
ParsedList);
parseClauses([], Stack, Parsed) -> Parsed ++ Stack.
run the code on terminal like so and get error:
tokeniser:parse([{bracket,open},
{prop,a},
{logicOp,'and'},
{prop,b},
{bracket,close}]).
** exception error: no function clause matching tokeniser:parseClauses([{prop,a},{logicOp,'and'},{prop,b},{bracket,close}],
[{bracket,open}],
[])
From the error message, you can tell that the function is being called like this:
tokeniser:parseClauses([{prop,a},{logicOp,'and'},{prop,b},{bracket,close}],
[{bracket,open}],
[])
This almost matches this clause:
parseClauses([{prop, Any},{logicOp, Any}| RestOfList], StackList, ParsedList) ->
But since Any is used twice in the argument list, this clause only matches when the two values are the same. In this call, they are different: a and 'and'.
You could change the two occurences of Any to something else, e.g. Prop and LogicOp, and the clause would accept two different values.
Related
I'm trying to implement the a split method in Erlang that is supposed to split a string like "i am on the mountain top" into a list like ["i","am","on","the","mountain","top"].
Here is my code (exercise.erl):
-module(exercise).
-import(oi,[read/1]).
-export([split/4]).
split(Text,_,Result,_) when Text == [] -> Result;
split([Head|Tail],Separator,Result,WordSummer) when Head == Separator ->
split(Tail,Separator,[Result|lists:flatten(WordSummer)],[]);
split([Head|Tail],Separator,Result,WordSummer) ->
split(Tail,Separator,Result,[WordSummer|Head]).
The problem I'm having is that when calling my exported function I get the following error:
9> c(exercise).
{ok,exercise}
10> exercise:split("sdffdgfdg dgdfgfg dgdfg dgdfgd dfgdfgdfgtrty hghfgh",$ ,[],[]).
** exception error: no function clause matching lists:do_flatten(103,[]) (lists.erl, line 627)
in function lists:do_flatten/2 (lists.erl, line 628)
in call from exercise:split/4 (exercise.erl, line 9)
11>
How can I solve this?
Two things:
The [WordSummer|Head] in the last line is creating an improper list because Head is an integer (one character of the input string). This is causing the error you're seeing. You probably meant [WordSummer, Head].
[Result|lists:flatten(WordSummer)] is creating a nested list instead of a list of strings. To append one item to a list, use ++ and wrap the right side in a list: Result ++ [lists:flatten(WordSummer)]
Final code:
split(Text,_,Result,_) when Text == [] -> Result;
split([Head|Tail],Separator,Result,WordSummer) when Head == Separator ->
split(Tail,Separator,Result ++ [lists:flatten(WordSummer)],[]);
split([Head|Tail],Separator,Result,WordSummer) ->
split(Tail,Separator,Result,[WordSummer, Head]).
Test:
1> c(exercise).
{ok,exercise}
2> exercise:split("sdffdgfdg dgdfgfg dgdfg dgdfgd dfgdfgdfgtrty hghfgh",$ ,[],[]).
["sdffdgfdg","dgdfgfg","dgdfg","dgdfgd","dfgdfgdfgtrty"]
There's still a bug where the last segment is being ignored. I'll let you figure that out (hint: you need to consider WordSummer in the first clause of the function).
I want to send all of the list content over TCP but i get some error
Code that send the list data
sendHistory(To, List) ->
lists:foreach(fun(#data{timestamp = T, data = D})->
gen_tcp:send(To, T),
gen_tcp:send(To, D)
end, List).
And I get this error.
Error in process <0.65.0> with exit value:
{function_clause,[{lists,foreach,
[#Fun<storage.0.129058542>,
{data,["1495971980"],
["\n\t",
["Jaam: ",
["Kuressaare linn"],
" Temperature: ",[]],
"\n\t",...]}],
[{file,"lists.erl"},{line,1337}]},
{storage,loop,4,[{file,"storage.erl"},{line,61}]}]}
The following line clause in your previous code is creating an improper list:
addToListIfNotAlreadyIn(New, [Old]) -> {[New | Old], ok};
You probably meant to write:
addToListIfNotAlreadyIn(New, [Old]) -> {[New, Old], ok};
The error message means that lists:foreach/2 was called with a second argument which was not a list. Since lists:foreach/2 recursively calls itself with the tail of the list after applying the function to the head, if an improper list is passed to the function, it will end up calling itself with a non-list argument on the last iteration:
1> lists:foreach(fun(X) -> ok end, [1, 2, 3 | 4]).
** exception error: no function clause matching lists:foreach(#Fun<erl_eval.6.118419387>,4) (lists.erl, line 1337)
I have the following code. I am checking 3 conditions. You can see for the first condition I stored the output of xml:get_tag_attr_s(...) in a variable and then used the variable within the if block. My problem is I get error illegal guard expression, if I try to do the above process in one line like I did for the other two conditions.
Also, I am getting variable '_' is unbound from the default condition. It supposed to be the same thing.
Can somebody please explain the issue?
validate_xmpp(Packet) ->
Type = xml:get_tag_attr_s(list_to_binary("type"), Packet),
if
(Type /= <<"chat">> ->
{error, "Message type is not chat"};
xml:get_path_s(Packet, [{elem, list_to_binary("body")}, cdata]) /= <<"">> ->
{error, "No or empty body"};
exml_query:path(Packet, [{element,<<"received">>},{attr,<<"xmlns">>}]) == <<"urn:xmpp:receipts">> ->
{error, "delivery-receipts should be ignored"};
_->
{ok, xml:get_tag_attr_s(list_to_binary("from"), Packet)}
end.
Erlang allows only these to be guards:
The atom true
Other constants (terms and bound variables), all regarded as false
Calls to the BIFs (built-in functions) specified in table Type Test BIFs
Term comparisons
Arithmetic expressions
Boolean expressions
Short-circuit expressions (andalso and orelse)
For more info take a look http://www.erlang.org/doc/reference_manual/expressions.html#id83606
Instead of _ use true. You cannot use _ in if, only in case statements, and also take a look at the docs.
isPrime(A,B) when B>math:sqrt(A) -> true;
That results in an illegal guard error.
On a first reading, it looks like the guard contains a "term comparison":
>
and an "arithmetic expression":
math:sqrt(A)
Futhermore, if you play around with the code, you will see that the guard:
B > A+2
is legal. So what's the difference between the "arithmetic expression" math:sqrt(A) and A+2?
The Erlang docs define an "arithmetic expression" as: `
+
-
*
/
div
rem
bnot
band
bor
bxor
bsl
bsr
Notably, math:sqrt() is not in the list of "arithmetic expressions". Therefore, math:sqrt(A) is a "function call" rather than an "arithmetic expression", and you can only call a certain limited number of functions in a guard, namely the "type test BIF's" listed here, such as:
is_integer/1
is_float/1
is_binary/1
is_list/1
is_map/1
is_function/1
etc.
I have this code:
-module(info).
-export([map_functions/0]).
-author("me").
map_functions() ->
{Mod,_} = code:all_loaded(),
map_functions(Mod,#{});
map_functions([H|Tail],A) ->
B = H:mod_info(exports),
map_functions(Tail,A#{H => B});
map_functions([],A) -> A.
However whenever I compile it I get a head mismatch on line 10 which is the
map_funtions([H|Tail],A) ->
I'm sure this is a very basic error but I just cannot get my head around why this does not run. It is a correct pattern match syntax [H|Tail] and the three functions with the same name but different arities are separated by commas.
Your function definition should be
map_functions() ->
{Mod,_} = code:all_loaded(),
map_functions(Mod, #{}).
map_functions([], A) -> A;
map_functions([H|Tail], A) ->
B = H:mod_info(exports),
map_functions(Tail, A#{H => B}).
The name map_functions is the same, but the arity is not. In the Erlang world that means these are two entirely different functions: map_functions/0 and map_functions/2.
Also, note that I put the "base case" first in map_functions/2 (and made the first clause's return value stick out -- breaking that to two lines is more common, but whatever). This is for three reasons: clarity, getting in the habit of writing the base case first (so you don't accidentally write infinite loops), and very often it is necessary to do this so you don't accidentally mask your base case by matching every parameter in a higher-precedence clause.
Some extended discussion on this topic is here (addressing Elixir and Erlang): Specify arity using only or except when importing function on Elixir
Function with same name but different arity are different, they are separated by dots.
code:all_loaded returns a list, so the first function should be written:
map_functions() ->
Mods = code:all_loaded(),
map_functions(Mods, #{}).
The resulting list Mods is a list of tuples of the form {ModName,BeamLocation} so the second function should be written:
map_functions([], A) -> A;
map_functions([{ModName,_}|Tail], A) ->
B = ModName:module_info(exports),
map_functions(Tail, A#{ModName => B}).
Note that you should dig into erlang libraries and try to use more idiomatic forms of code, the whole function, using list comprehension, can be written:
map_functions() ->
maps:from_list([{X,X:module_info(exports)} || {X,_} <- code:all_loaded()]).
So I've been using Erlang for the last eight hours, and I've spent two of those banging my head against the keyboard trying to figure out the exception error my console keeps returning.
I'm writing a dice program to learn erlang. I want it to be able to call from the console through the erlang interpreter. The program accepts a number of dice, and is supposed to generate a list of values. Each value is supposed to be between one and six.
I won't bore you with the dozens of individual micro-changes I made to try and fix the problem (random engineering) but I'll post my code and the error.
The Source:
-module(dice2).
-export([d6/1]).
d6(1) ->
random:uniform(6);
d6(Numdice) ->
Result = [],
d6(Numdice, [Result]).
d6(0, [Finalresult]) ->
{ok, [Finalresult]};
d6(Numdice, [Result]) ->
d6(Numdice - 1, [random:uniform(6) | Result]).
When I run the program from my console like so...
dice2:d6(1).
...I get a random number between one and six like expected.
However when I run the same function with any number higher than one as an argument I get the following exception...
**exception error: no function clause matching dice2:d6(1, [4|3])
... I know I I don't have a function with matching arguments but I don't know how to write a function with variable arguments, and a variable number of arguments.
I tried modifying the function in question like so....
d6(Numdice, [Result]) ->
Newresult = [random:uniform(6) | Result],
d6(Numdice - 1, Newresult).
... but I got essentially the same error. Anyone know what is going on here?
This is basically a type error. When Result is a list, [Result] is a list with one element. E.g., if your function worked, it would always return a list with one element: Finalresult.
This is what happens (using ==> for "reduces to"):
d6(2) ==> %% Result == []
d6(2, [[]]) ==> %% Result == [], let's say random:uniform(6) gives us 3
d6(1, [3]) ==> %% Result == 3, let's say random:uniform(6) gives us 4
d6(0, [4|3]) ==> %% fails, since [Result] can only match one-element lists
Presumably, you don't want [[]] in the first call, and you don't want Result to be 3 in the third call. So this should fix it:
d6(Numdice) -> Result = [], d6(Numdice, Result). %% or just d6(Numdice, []).
d6(0, Finalresult) -> {ok, Finalresult};
d6(Numdice, Result) -> d6(Numdice - 1, [random:uniform(6) | Result]).
Lesson: if a language is dynamically typed, this doesn't mean you can avoid getting the types correct. On the contrary, it means that the compiler won't help you in doing this as much as it could.