Erlang: proplists:get_value/2 or pattern matching? - erlang

I have a list of tuples that has always the same form (i.e. the tuples come always in the same order):
1> L = [{a, 1}. {b,2}, {c, 3}, {d, 4}].
Knowing that the list has only a few elements, what is the best way to extract the values associated to the keys?
Suppose the list is passed as argument to a function, to extract the values should I use:
proplists:get_value(a, L).
proplists:get_value(b, L).
...
proplists:get_valus(d, L).
Or should I simply use pattern matching as:
[{a, 1}. {b,2}, {c, 3}, {d, 4}] = L.

If you really know your lists is in same form pattern matching is simplest
[{a, A}, {b, B}, {c, C}, {d, D}] = L,
you can compare it with following
[A, B, C, D] = [ proplists:get_value(X, L) || X <- [a,b,c,d] ],
or
A = proplists:get_value(a, L),
B = proplists:get_value(b, L),
C = proplists:get_value(c, L),
D = proplists:get_value(d, L),
or
[A, B, C, D] = [ V || Key <- [a,b,c,d], {K, V} <- L, K =:= Key ],
Pattern matching will be also fastest. You can also use lists:keyfind/3 which is implemented as Bif and is way faster than proplist:get_value/2 but it doesn't matter for short lists.

Related

Erlang generic foldl/3 equivalent for binary trees

I need to write the equivalent of lists:foldl/3 for binary trees.
Every node is represented as:
[Value, LeftNode, RightNode]
So a tree with a root 2 and leaves 1 and 3 would look like this:
[2, [1, [], []], [3, [], []]]
Later I want to use that function to perform operations, such as summing up all positive values in that tree etc.
This is the function I wrote:
tree_foldl(_, Acc, []) -> Acc;
tree_foldl(Fun, Acc, [V, L, R]) ->
X1 = tree_foldl(Fun, Fun(V, Acc), L),
X2 = tree_foldl(Fun, Fun(L, X1), R),
X2.
the problem is that it's not truly generic, and when we have, let's say a tree with only a root, e.g.:
[2, [], []]
it calls both Fun(2, 0) and Fun([], 2) and when trying to sum up the values in the second case it tries to do [] + 2, which is an invalid expression. It also would break on more complicated cases.
I would greatly appreciate any help with fixing this function. Thank you.
First of all, you should use tuples as nodes in this case instead of lists. Lists are linked lists between their elements while tuples use contiguous memory (you can access a single element of a tuple without processing the leading elements of structure).
There's no need for Fun(L, X1):
tree_foldl(_, Acc, []) -> Acc;
tree_foldl(Fun, Acc, [V, L, R]) ->
Acc0 = Fun(V, Acc),
Acc1 = tree_foldl(Fun, Acc0, L),
Acc2 = tree_foldl(Fun, Acc1, R),
Acc2.
If the node is empty, do nothing, else run the Fun on the node and recurse on both subtrees.

MergeSort and Quicksort in Erlang not working

Can someone help me figure out what's wrong with my sorting algorithms. I get no errors but get stuck in some kind of infinite loop. The functions seem to work individually.
msort([])->
[];
msort(L)->
{L3, L4} = msplit(L, [],[]),
merge(msort(L3), msort(L4)).
msplit([], L1, L2)->
{L1, L2};
msplit([H|[]], L1, L2)->
msplit([], [H]++L1, L2);
msplit([H|[H2|T]], A, B)->
msplit(T, A++[H], B++[H2]).
merge(L, [])->L;
merge([], R)->R;
merge([H1|T1], [H2|T2])->
if H1 < H2
-> [H1|merge(T1, [H2|T2])];
true-> [H2|merge([H1|T1], T2)]
end.
qsort([])->[];
qsort([H|T])->
{A, B} =qsplit(T, H, [], []),
Small =qsort(A),
Large = qsort(B),
lists:append(Small,Large).
qsplit([], H, A, B)->
{A++[H], B};
qsplit([H|T], P, A, B)->
if H > P->
qsplit(T, P, A++[H], B);
true-> qsplit(T, P, A, B++[H])
end.
After some changes the code is working properly:
msort([]) ->
[];
msort([_] = L) ->
L;
msort(L)->
{L3, L4} = msplit(L, [],[]),
merge(msort(L3), msort(L4)).
msplit([], L1, L2)->
{L1, L2};
msplit([H|[]], L1, L2)->
msplit([], [H|L1], L2);
msplit([H|[H2|T]], A, B)->
msplit(T, [H|A], [H2|B]).
merge(L, [])->L;
merge([], R)->R;
merge([H1|T1], [H2|T2])->
if H1 < H2
-> [H1|merge(T1, [H2|T2])];
true-> [H2|merge([H1|T1], T2)]
end.
qsort([])->[];
qsort([_] = L)->L;
qsort([H|T])->
{A, B} =qsplit(T, H, [], []),
Large =qsort(A),
Small = qsort(B),
lists:append(Small,[H|Large]).
qsplit([], _, A, B)->
{A, B};
qsplit([H|T], P, A, B)->
if H > P->
qsplit(T, P, [H|A], B);
true-> qsplit(T, P, A, [H|B])
end.
If you call msort/1 with a list containing just one item [X] your msplit/1 will return {[X], []} where you call msort/1 with one item [X] and so on. You can fix it by adding msort/1 function clause:
msort([])->
[];
msort([_] = L) ->
L;
msort(L)->
...
A similar problem is in your qsort/1.
There are more problems in your code. You should replace all your A++[H] with [H] ++ A which is even better written as [H|A]. It has big impact to an efficiency of your code. You can use [H, H2 | T] instead of [H | [H2 | T]], it is nice syntactic sugar which helps readability.

Erlang Maps : get keys that has the same value

I have a {H,VV} pair and I want to compare this pair against the rest of the Map to find other key that has the same value.
I tried this:
check(H,Map)->
VV=maps:get(H,Map),
Fun = fun(K,V) when H =/= K, V=:=VV->
io:format("~p~p~n",[H,K])
end,
maps:map(Fun,Map).
it compiles but raise error "function_clause"
Any ideas how to implement this?
The function you're passing to maps:map/2 does not handle the H key nor any value not equal to VV. Try this instead:
check(H,Map)->
VV=maps:get(H,Map),
Fun = fun(K,V) when H =/= K, V=:=VV->
io:format("~p:~p~n",[H,K]),
V;
(_,V) -> V
end,
maps:map(Fun,Map).
I think you're better off using maps:fold/3 for this case, though, since you're not trying to create a new map, but rather just want to know the other keys with the same value as H. Consider the approach below:
check(H, Map) ->
VV = maps:get(H,Map),
maps:fold(fun(K,V,Acc) when K /= H, V =:= VV ->
[K|Acc];
(_,_,Acc) ->
Acc
end, [], Map).
This version returns a list of keys that have the same value as H in Map.
There are list comprehension solutions for this as well:
VV = maps:get(H,Map),
[ K || K <- maps:keys(Map), K =/= H, VV =:= maps:get(K, Map) ].
Or
VV = maps:get(H,Map),
[ K || {K, V} <- maps:to_list(Map), K =/= H, V =:= VV ].

Update list's values

I have the following setup:
1> rd(rec, {name, value}).
rec
2> L = [#rec{name = a, value = 1}, #rec{name = b, value = 2}, #rec{name = c, value = 3}].
[#rec{name = a,value = 1},
#rec{name = b,value = 2},
#rec{name = c,value = 3}]
3> M = [#rec{name = a, value = 111}, #rec{name = c, value = 333}].
[#rec{name = a,value = 111},#rec{name = c,value = 333}]
The elements in list L are unique based on their name. I also don't know the previous values of the elements in list M. What I am trying to do is to update list L with the values in list M, while keeping the elements of L that are not present in M. I did the following:
update_values([], _M, Acc) ->
Acc;
update_attributes_from_fact([H|T], M, Acc) ->
case [X#rec.value || X <- M, X#rec.name =:= H#rec.name] of
[] ->
update_values(T, M, [H|Acc]);
[NewValue] ->
update_values(T, M, [H#rec{value = NewValue}|Acc])
end.
It does the job but I wonder if there is a simpler method that uses bifs.
Thanks a lot.
There's no existing function that does this for you, since you just want to update the value field rather than replacing the entire record in L (like lists:keyreplace() does). If both L and M can be long, I recommend that if you can, you change L from a list to a dict or gb_tree using #rec.name as key. Then you can loop over M, and for each element in M, look up the correct entry if there is one and write back the updated record. The loop can be written as a fold. Even if you convert the list L to a dict first and convert it back again after the loop, it will be more efficient than the L*M approach. But if M is always short and you don't want to keep L as a dict in the rest of the code, your current approach is good.
Pure list comprehensions solution:
[case [X||X=#rec{name=XN}<-M, XN=:=N] of [] -> Y; [#rec{value =V}|_] -> Y#rec{value=V} end || Y=#rec{name=N} <- L].
little bit more effective using lists:keyfind/3:
[case lists:keyfind(N,#rec.name,M) of false -> Y; #rec{value=V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].
even more effective for big M:
D = dict:from_list([{X#rec.name, X#rec.value} || X<-M]),
[case dict:find(N,D) of error -> Y; {ok,V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].
but for really big M this approach can be fastest:
merge_join(lists:keysort(#rec.name, L), lists:ukeysort(#rec.name, M)).
merge_join(L, []) -> L;
merge_join([], _) -> [];
merge_join([#rec{name=N}=Y|L], [#rec{name=N, value=V}|_]=M) -> [Y#rec{value=V}|merge_join(L,M)];
merge_join([#rec{name=NL}=Y|L], [#rec{name=NM}|_]=M) when NL<NM -> [Y|merge_join(L,M)];
merge_join(L, [_|M]) -> merge_join(L, M).
You could use lists:ukeymerge/3:
lists:ukeymerge(#rec.name, M, L).
Which:
returns the sorted list formed by merging TupleList1 and TupleList2.
The merge is performed on the Nth element of each tuple. Both
TupleList1 and TupleList2 must be key-sorted without duplicates prior
to evaluating this function. When two tuples compare equal, the tuple
from TupleList1 is picked and the one from TupleList2 deleted.
A record is a tuple and you can use #rec.name to return the position of the key in a transparent way. Note that I reverted the lists L and M, since the function keeps the value from the first list.

List comprehension AND guards

How to implement AND guards in list comprehensions? Separating the guards with comma seems to word as OR:
1> rd(r, {a, b}).
r
2> L = [#r{a = 1, b =2}, #r{a = 1, b = 3}].
[#r{a = 1,b = 2},#r{a = 1, b = 3}]
3> [X || X <- L, X#r.a =/= 1, X#r.b =/= 2].
[]
Thanks a lot.
That's definitely an AND. The first element fails both tests; the second fails the X#r.a =/= 1 test.
If you want OR, simply use the orelse operator:
2> [X || X <- L, X#r.a =/= 1 orelse X#r.b =/= 2].
[#r{a = 1,b = 3}]

Resources