comparison using if with erlang - erlang

I work with erlang
I want to make a function that will check if the Cin and Id is not null
I tried with:
if Cin /= null && Id/=null -> {ok,Cin et Id sont différents de null};
true -> {nok,Cin et Id sont null}
end.
I know that the notion of '&&' does not exist in erlang
but I can not find the equivalent of this notion in erlang

In Erlang, use andalso instead of &&:
if Cin /= null andalso Id/=null -> {ok,Cin et Id sont différents de null};
The use of andalso is short-circuiting and is equivalent to &&. The regular and operator always evaluates both sides of the expression and is not short-circuiting.

Usually, it is better to use a match:
case {Cin, Id} of
{null, _} -> cin_null;
{_, null} -> id_null;
{_, _} -> not_null
end
But also note that you can get away with not checking at all. Add a guard in the function head:
my_func(Cin, Id) when is_integer(Cin), is_binary(Id) ->
do_something.
If this fails to match, you have a crash, but this is usually what you expect to happen in the code base.

You can create function and use pattern matching:
is_null(null, null) ->
true;
is_null(_, _) ->
false.
in console:
1> c(some_mod).
{ok,some_mod}
2> some_mod:is_null(null, 1).
false
3> some_mod:is_null(1, 1).
false
4> some_mod:is_null(null, null).
true

Related

How to case switch in erlang using a function value?

This program is crashing despite seeming to work. I don't understand why though. I'm trying to accomplish a deep merge and need conditional logic.
Given the following list:
ManOne = #{ "Bob" => #{"Sagget" => #{}} }
ManTwo = #{ "Bob" => #{"Daniels" => #{}} }
I'm trying to compare them as follows, this function returns true as expected:
check_if_same(M1, M2) ->
{ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.
merger(M1, M2) ->
M1_Keys = maps:keys(M1),
M2_Keys = maps:keys(M2),
do_merge(M1, M2, M1_Keys).
do_merge(M1, M2, [Head|Tail]) ->
Check = check_if_same(M1, M2),
io:fwrite("Check is: ~p\n", [Check]),
case Check of
{ok, true} ->
io:fwrite("true\n");
{ok, false} ->
io:fwrite("false\n")
end,
do_merge(M1, M2, Tail);
do_merge(M1, M2, []) ->
ok.
check_if_same(M1, M2) ->
{ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.
gives the following output:
Check is: {ok,true}
true
{"init terminating in do_boot",{{badmap,ok},[{maps,keys,[ok],[]},{helloworld,merger,2,[{file,"helloworld.erl"},{line,9}]},{init,start_em,1,[]},{init,do_boot,3,[]}]}}
init terminating in do_boot ()
Crash dump is being written to: erl_crash.dump...done
Let's go over your question first because there's a few misconceptions and/or corrections that we can make.
Your Description
This program is crashing despite seeming to work. I don't understand why though. I'm trying to accomplish a deep merge and need conditional logic.
Given the following list:
ManOne = #{ "Bob" => #{"Sagget" => #{}} }
ManTwo = #{ "Bob" => #{"Daniels" => #{}} }
Note that the above are NOT lists, they are maps, which function entirely differently.
A map is, for all intents and purposes, a lookup table until it contains ~31 key/value pairs.
At this point, it becomes a HashMap (this can be seen by viewing the elements as they become unordered after the map becomes a HashMap).
I'm trying to compare them as follows, this function returns true as expected:
check_if_same(M1, M2) ->
{ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.
This is an incorrect way to assert equality; in erlang, it is suggested to not use == to check equality.
Instead, =:= should be used.
The reason for this is due to the fact that == does NOT check the type of the elements it is comparing and only takes a fuzzy value - i.e 1 == 1.0 will return true but 1 =:= 1.0 will return false.
Personally, I would recommend instead using Erlang's Pattern-Matching to check your values.
This could be implemented using the following snippet:
-spec check_if_same(M1 :: map(), M2 :: map()) -> boolean().
check_if_same(M1, M2) ->
SortedKeys1 = lists:sort(maps:keys(M1)),
SortedKeys2 = lists:sort(maps:keys(M2)),
%% We hide the implementation of the function in
%% a function with the same name suffixed with an
%% underscore. This allows us to have a public api
%% but keep the implementation internal which allows
%% the code to be a bit cleaner.
check_if_same_(SortedKeys1, SortedKeys2).
%% If they're both empty then we've gone through
%% every key meaning that they must be identical
check_if_same_([], []) ->
true;
%% If the current Key on both heads is the same
%% then recurse and check the next and so on
check_if_same_([Key|Tail1], [Key|Tail2]) ->
check_if_same_(Tail1, Tail2);
%% If we get anything else, e.g more keys in
%% one than the other or the keys don't match,
%% then we'll fall in to this case.
%% As we know anything that falls in to this
%% case doesn't match, we just return false
check_if_same_(Keys1, Keys2) when is_list(Keys1), is_list(Keys2) ->
false.
Note that in the above snippet, I only ever returned true or false - my recommendation for cleaner code would be to keep to the following formats;
ok - This is typically for functions where you care about the effect and not the return
true | false - This is typically for comparison functions, i.e is_binary/1, is_function/1
{ok, Value} - This would typically be for any function where you care about the value returned
{error, Reason} - This would be used whenever you expect an error so that you can bubble the error back up the chain with an easy-to-match format
Your Code Snippet
merger(M1, M2) ->
M1_Keys = maps:keys(M1),
%% Note that you don't use the M2Keys here so you don't need to do the work to get them
M2_Keys = maps:keys(M2),
do_merge(M1, M2, M1_Keys).
do_merge(M1, M2, [Head|Tail]) ->
Check = check_if_same(M1, M2),
%% It's generally recommended to stick to io:format/2 rather than io:fwrite/2
io:fwrite("Check is: ~p\n", [Check]),
case Check of
{ok, true} ->
io:fwrite("true\n");
{ok, false} ->
io:fwrite("false\n")
end,
do_merge(M1, M2, Tail);
do_merge(M1, M2, []) ->
ok.
check_if_same(M1, M2) ->
{ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.
Now, the above snippet (other than being a bit inefficient) is perfectly okay erlang and will work as expected
gives the following output:
Check is: {ok,true}
true
{"init terminating in do_boot",{{badmap,ok},[{maps,keys,[ok],[]},{helloworld,merger,2,[{file,"helloworld.erl"},{line,9}]},{init,start_em,1,[]},{init,do_boot,3,[]}]}}
init terminating in do_boot ()
Crash dump is being written to: erl_crash.dump...done
This crash dump is where the real issue is;
Check is: {ok,true}
true
From this we can tell that we
Hit the io:fwrite/2 (io:fwrite("Check is: ~p\n", [Check]))
Entered the {ok, true} path in the case (io:fwrite("true\n"))
The next line is where we see the actual problem, let's break it down:
"init terminating in do_boot" - We failed when starting up, this might be when running an escript or starting an app
Now let's break down that tuple:
{
{badmap,ok}, %% The function we called expected a map and we passed in 'ok'
[
{maps,keys,[ok],[]}, %% We called maps:keys/1 with 'ok' as an arg
{helloworld,merger,2,[{file,"helloworld.erl"},{line,9}]}, %% This was called at helloworld:merger/2 (helloworld.erl:9)
{init,start_em,1,[]},{init,do_boot,3,[]} %% We failed on start up
]
}
What we can take away from this is that you're calling merger in your code with an invalid value of ok on line 9 of helloworld.erl
There is some missing information. Although this code looks like a first draft or step, it works as expected. I tested it in the shell and got this:
-module (merger).
-compile(export_all).
merger(M1, M2) ->
M1_Keys = maps:keys(M1),
M2_Keys = maps:keys(M2),
do_merge(M1, M2, M1_Keys).
do_merge(M1, M2, [Head|Tail]) ->
Check = check_if_same(M1, M2),
io:fwrite("Check is: ~p\n", [Check]),
case Check of
{ok, true} ->
io:fwrite("true\n");
{ok, false} ->
io:fwrite("false\n")
end,
do_merge(M1, M2, Tail);
do_merge(M1, M2, []) ->
ok.
check_if_same(M1, M2) ->
{ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.
test() ->
merger(#{ "Bob" => #{"Sagget" => #{}} },#{ "Bob" => #{"Daniels" => #{}} }).
which gives:
8> c(merger).
merger.erl:3: Warning: export_all flag enabled - all functions will be exported
merger.erl:7: Warning: variable 'M2_Keys' is unused
merger.erl:9: Warning: variable 'Head' is unused
merger.erl:19: Warning: variable 'M1' is unused
merger.erl:19: Warning: variable 'M2' is unused
{ok,merger}
9> merger:test().
Check is: {ok,true}
true
ok
10>
Maybe you could also tell us what is the expected result of merging ManOne and ManTwo

Erlang - pattern matching predicate

To know if particular expression matches specified pattern I can write in erlang something like following:
case <expression> of
<pattern> -> true;
_ -> false
end
For example:
case {1, 2, 3} of
{_, 2, _} -> true;
_ -> false
end
Is there a way to write it in more compact way? Something like:
is_match(<pattern>, <expression>)
No, there's no such construct. You could define a macro to do it:
-define(is_match(Pattern, Expr), case Expr of Pattern -> true; _ -> false end).
another macro :o) -define (IS_MATCH(PAT,EXP), catch(PAT = EXP) == EXP end). with no case, but I am not sure that a catch is better.

Erlang - Searching for tuples within tuples

Using Erlang, I have the following expression:
{add,{var,a},{mul,{num,2},{var,b}}}
and I am using lists:keymember to see whether the letter b is within the expression as such:
lists:keymember(b,2,[expr])
However, it doesn't look within the third tuple '{mul,{num,2},{var,b}' as that is a separate tuple. Is there a function that will search through the whole tuple and tuples within?
Thanks
As far I as I know there are no such functions. Probably you will have to implement some custom solution using recursion. Here is my example:
-module(test).
-compile(export_all).
find(_, []) -> false;
find(E, T) when is_tuple(T) ->
find(E, tuple_to_list(T));
find(E, [H|T]) ->
case find(E, H) of
false -> find(E, T);
true -> true
end;
find(V, E) -> V == E.
And usage:
1> test:find(b, {add,{var,a},{mul,{num,2},{var,b}}}).
true
2> test:find(b, {add,{var,a},{mul,{num,2},{var,c}}}).
false
Please review your code.
Line1: this is a tree, not a list.
Line2: expr is not a variable.
What you want to do is a visitor function, and you'll have to write it yourself.
A very good start would be to read this.

Matching tuples with don't-care variables in Erlang

I am looking for a way to find tuples in a list in Erlang using a partial tuple, similarly to functors matching in Prolog. For example, I would like to following code to return true:
member({pos, _, _}, [..., {pos, 1, 2}, ...])
This code does not work right away because of the following error:
variable '_' is unbound
Is there a brief way to achieve the same effect?
For simple cases it's better to use already mentioned lists:keymember/3. But if you really need member function you can implement it yourself like this:
member(_, []) ->
false;
member(Pred, [E | List]) ->
case Pred(E) of
true ->
true;
false ->
member(Pred, List)
end.
Example:
>>> member(fun ({pos, _, 2}) -> true; (_) -> false end, [..., {pos, 1, 2}, ...]).
Use lists:keymember/3 instead.
You can do it with a macro using a list comprehension:
-define(member(A,B), length([0 || A <- B])>0).
?member({pos, _, _}, [{width, 17, 42}, {pos, 1, 2}, totally_irrelevant]).
It is not very efficient (it runs through the whole list) but it is the closest I can think to the original syntax.
If you want to actually extract the elements that match you just remove 'length' and add a variable:
-define(filter(A,B), [_E || A =_E <- B]).
You could do it using list comprehension:
Matches = [ Match || {Prefix, _, _} = Match <- ZeList, Prefix == pos].
Another possibility would be to do what match specs do and use the atom '_' instead of a raw _. Then, you could write a function similar to the following:
member(X, List) when is_tuple(X), is_list(List) ->
member2(X, List).
% non-exported helper functions:
member2(_, []) ->
false;
member2(X, [H|T]) when not is_tuple(H); size(X) =/= size(H) ->
member2(X, T);
member2(X, [H|T]) ->
case is_match(tuple_to_list(X), tuple_to_list(H)) of
true -> true;
false -> member2(X, T)
end.
is_match([], []) ->
true;
is_match(['_'|T1], [_|T2]) ->
is_match(T1, T2);
is_match([H|T1], [H|T2]) ->
is_match(T1, T2);
is_match(_, _) ->
false.
Then, your call would now be:
member({pos, '_', '_'}, [..., {pos, 1, 2}, ...])
This wouldn't let you match patterns like {A, A, '_'} (checking where the first two elements are identical), but if you don't need variables this should work.
You could also extend it to use variables using a similar syntax to match specs ('$1', '$2', etc) with a bit more work -- add a third parameter to is_match with the variable bindings you've seen so far, then write function clauses for them similar to the clause for '_'.
Granted, this won't be the fastest method. With the caveat that I haven't actually measured, I expect using the pattern matching in the language using a fun will give much better performance, although it does make the call site a bit more verbose. It's a trade-off you'll have to consider.
May use ets:match:
6> ets:match(T, '$1'). % Matches every object in the table
[[{rufsen,dog,7}],[{brunte,horse,5}],[{ludde,dog,5}]]
7> ets:match(T, {'_',dog,'$1'}).
[[7],[5]]
8> ets:match(T, {'_',cow,'$1'}).
[]

Case issue in erlang

Working with Erlang's case, I'm facing a problem. The problem is the following:
other languages:
switch(A)
{
case "A" : case "B" :
//do something
break;
}
So, how to achieve the same thing using Erlang? Because sometimes it is very important to put conditions like these, to avoid overhead.
May be guards are what you want.
the_answer_is(N) when A == "A"; A == "B";
; - is OR
, - is AND
You can use case expressions in Erlang. The syntax is:
case Expression of
Pattern1 [when Guard1] -> Expr_seq1;
Pattern2 [when Guard2] -> Expr_seq2;
...
end
To quote Pragmatic Erlang:
case is evaluated as follows. First,
Expression is evaluated; assume this
evaluates to Value. Thereafter, Value
is matched in turn against Pattern1
(with the optional guard Guard1),
Pattern2, and so on, until a match is
found. As soon as a match is found,
then the corresponding expression
sequence is evaluated—the result of
evaluating the expression sequence is
the value of the case expression. If
none of the patterns match, then an
exception is raised.
An example:
filter(P, [H|T]) ->
case P(H) of
true -> [H|filter(P, T)];
false -> filter(P, T)
end;
filter(P, []) ->
[].
filter(P , L); returns a list of all those elements X in L for which P(X) is true. This can be written using pattern matching, but the case construct makes the code cleaner. Note that choosing between pattern matching and case expressions is a matter of taste, style and experience.
Not my favorite style, but you can do something like:
case A of
_ when A == "A";
A == "B" -> do_ab();
_ when A == "C";
_ when A == "D" -> do_cd();
_ -> do_default()
end.

Resources