I have two lists of key-value pairs ([{key_1, value_1}, ..., {key_n, value_n}]). What is the best method of updating first list with the second one? For example:
1> extend([{1, "one"}, {2, "too"}], [{2, "two"}, {3, "three"}]).
[{1, "one"}, {2, "two"}, {3, "three"}]
I've found just two similar function: lists:keystore/4, which updates a single tuple, and lists:keymerge, which merges two lists without removing key duplicates.
I've found the answer myself. Erlang's orddict module deals with pure sorted list of {key, value} pairs. So, extend function can be defined as follows:
extend(L1, L2) ->
orddict:merge(fun(_Key, _V1, V2) -> V2 end, L1, L2).
If L1 and L2 aren't initially sorted, then they can be transformed to orddicts with orddict:from_list/1.
for instance
extend(L1,L2) ->
lists:foldl(fun({K,V},Acc) -> lists:keystore(K,1,Acc,{K,V}) end, L1,L2).
or
extend(L1,L2) ->
compact(lists:keysort(1,L1++L2),[]).
compact([],Acc) -> lists:reverse(Acc);
compact([{K,_},{K,V}| Rest], Acc) -> compact([{K,V} |Rest],Acc);
compact([X|Rest],Acc) -> compact(Rest,[X|Acc]).
Related
I have a task: "Add second and fifth elements of list to the end of the list and remove the penultimate element". I need to do this without using the module lists, just using recursion.
I have code which finds an element by index, which solves the problem of finding the second and fifth element (also not an ideal solution for me, because it's the same lists:nth function).
-module(task).
-export([remove_and_add/1]).
remove_and_add(List) ->
remove_penultimate(List) ++ [nth(2, List)] ++ [nth(5, List)].
nth(1, [H|_]) ->
H;
nth(N, [_|T]) ->
nth(N - 1, T).
But I don't understand how to remove the penultimate element without a length (how to implement remove_penultimate function).
Whenever you use ++, you can pretty much assume you are doing it inefficiently:
remove_penultimate(List) ++ [nth(2, List)] ++ [nth(5, List)].
If you can't use the lists module, then the first function every beginner needs to write is a reverse(List) function. Beside being one of the most useful functions, that will teach you the trick of how to add an "accumulator" variable to a function's parameter variables to store whatever data you want in it. Here is an example:
go(List) ->
go(List, []).
go([H|T], Acc) -> %% Acc starts off as a blank list, which can be used to store data
%% maybe do something here
go(T, [H|Acc]); %% storing data in the Acc list
go([], Acc) -> Acc.
You can add as many variables as you need to a function's parameters using the trick above, for instance:
go(List) ->
go(List, 1, none, none, []).
go([H|T], N, X, Y, Acc) -> ...
Examine this function:
show_previous(List) ->
show_previous(List, none).
show_previous([], Prev) -> %% A variable called Prev has been added to the function's parameters, and it starts off with the value none.
io:format("current: end of list, previous: ~w~n", [Prev]);
show_previous([Last], Prev) -> %% This clause only matches a list with one element.
io:format("current: ~w, penultimate: ~w~n", [Last, Prev]),
show_previous([H|T], Prev) ->
io:format("current: ~w, prev: ~w~n", [H, Prev]),
show_previous(T, H).
Note that in erlang, a function with the same name but with a different number of parameter variables (known as the "arity" of a function) is a completely different function, so show_previous/1 and show_previous/2 are completely different functions.
Here's another example of what you can do:
show([]) ->
io:format("!No more elements!~n");
show([X, Y]) -> %% only matches a list with two elements
io:format("penultimate: ~w, last: ~w~n", [X, Y]),
show([Y]);
show([H|T]) ->
io:format("current: ~w~n", [H]),
show(T). %% By whittling the list down one element at a time,
%% the list will eventually become a 2 element list
But I don't understand how to remove the penultimate element without a length.
You could always get the length by recursing over all the elements and counting them. It would be good practice to write your own length function. It's very simple. Remember though, it's more efficient to recurse over a list as few times as possible, and you should be able to solve your original problem by recursing over the list once, then reversing the list (which requires recursing over the list again).
Another possible approach to removing the penultimate element is to reverse the list, then ask yourself, "How do I remove the second element of a list?"
I came up with a solution that traverses the original list once, and when it comes to the end of the list, it reverses the Acc list and returns it. It takes this list:
[1, 2, 3, 4, 5, 6, 7]
and returns this list:
[1,3,4,7,2,5]
I would describe that operation as removing the penultimate element in the list and moving the 2nd and 5th elements to the end of the list. Copying the 2nd and 5th elements and adding the copies to the end of the list would be solved in a similar fashion.
I am trying to create a method that takes an associative and commutative operator, as well a list of values, and then returns the answer by applying an operator to the values in the list.
The following two examples represent what the input/output are supposed to look like.
Example 1
Input: sum(fun(A,B) -> A+B end, [2,6,7,10,12]).
Output: 37
Example 2
Input: sum(fun (A,B) -> A++B end , ["C", "D", "E"]).
Output: "CDE"
This is the code I am working with so far.
-module(tester).
-compile(export_all).
sum(Func, Data, Acc) ->
lists:foldr(Func, Acc, Data).
This code produces the correct result, however, there are two problems I am trying to figure out how to approach answering.
(1) In order for this code to work, it requires an empty list to be included at the end of the command line statements. In other words, if I enter the input above (as in the examples), it will err out, because I did not write it in the following way:
12> tester:sum(fun(X, Acc) -> X+Acc end, [2,6,7,10,12], 0).
How would I implement this without an empty list as in the examples above and get the same result?
(2) Also, how would the code be implemented without the list function, or in an even more serial way?
How would I implement this without an empty list as in the examples above and get the same result?
Assuming the list always has one element (you can't really do it without this assumption), you can extract the first element from the list and pass that as the initial accumulator. You'll need to switch to foldl to do this efficiently. (With foldr you'll essentially need to make a copy of the list to drop the last element.)
sum(Func, [X | Xs]) ->
lists:foldl(fun (A, B) -> Func(B, A) end, X, Xs).
1> a:sum(fun(A,B) -> A+B end, [2,6,7,10,12]).
37
2> a:sum(fun (A,B) -> A++B end , ["C", "D", "E"]).
"CDE"
Also, how would the code be implemented without the list function, or in an even more serial way?
Here's a simple implementation using recursion and pattern matching:
sum2(Func, [X | Xs]) ->
sum2(Func, Xs, X).
sum2(Func, [], Acc) ->
Acc;
sum2(Func, [X | Xs], Acc) ->
sum2(Func, Xs, Func(Acc, X)).
We define two versions of the function. The first one extracts the head and uses that as the initial accumulator. The second one, with arity 3, does essentially what the fold functions in lists do.
After working on this for a while, this was my solution. I've left some comments about the general idea of what I did, but there's a lot more to be said.
-module(erlang2).
-compile(export_all).
-export([reduce/2]).
reduce(Func, List) ->
reduce(root, Func, List).
%When done send results to Parent
reduce(Parent, _, [A]) ->
%send to parent
Parent ! { self(), A};
%I tried this at first to take care of one el in list, but it didn't work
%length ([]) ->
% Parent ! {self(), A};
%get contents of list, apply function and store in Parent
reduce(Parent, Func, List) ->
{ Left, Right } = lists:split(trunc(length(List)/2), List),
Me = self(),
%io:format("Splitting in two~n"),
Pl = spawn(fun() -> reduce(Me, Func, Left) end),
Pr = spawn(fun() -> reduce(Me, Func, Right) end),
%merge results in parent and call Func on final left and right halves
combine(Parent, Func,[Pl, Pr]).
%merge pl and pl and combine in parent
combine(Parent, Func, [Pl, Pr]) ->
%wait for processes to complete (using receive) and then send to Parent
receive
{ Pl, Sorted } -> combine(Parent, Func, Pr, Sorted);
{ Pr, Sorted } -> combine(Parent, Func, Pl, Sorted)
end.
combine(Parent, Func, P, List) ->
%wait and store in results and then call ! to send
receive
{ P, Sorted } ->
Results = Func(Sorted, List),
case Parent of
root ->
Results;
%send results to parent
_ -> Parent ! {self(), Results}
end
end.
I have the following list of items
[{id, user1, category1}, {id, user2, category1}, {id, user1, category2}....],
where id is unique, and user/category can be repeated. I am trying to figure out how to get stats from the list, e.g.
[{user1, category1, 20}, {user1, category2, 30}..]
You can do it using the lists:foldl/3 function.
F = fun({_,User,Cat},Accumulator) ->
N = maps:get({User,Cat},Accumulator,0),
maps:put({User,Cat},N+1,Accumulator) end.
CountMap = lists:foldl(F,#{},InputListe),
this returns a map of the form #{{user1, category1} => 20, {user1, category2} => 30 ...}
if you really need a list then you have to transform the map:
CountList = maps:fold(fun({User,Cat}, Count, Acc) -> [{User,Cat,Count}|Acc] end,[],CountMap).
I have used an intermediary Map because if the input list is big, then it gives fast accesses and fast update compare to a solution where you work directly in the output list. It costs a lot to retrieve information in a list (in average parse half of the list), and it costs also a lot to modify it (in average copy half of the list
for an input list of 200 000 elements, it took 94msec to generate the map and convert it into a list on my laptop, and 219ms for 500000 elements.
Although Pascal's solution is a good universal solution, for small datasets (like up to 15 000) you can use this version using lists:sort/1 which is significantly faster for them.
main(L) ->
count(lists:sort(transform(L))).
count([]) -> [];
count([H|T]) ->
count(H, T, 1, []).
count(H, [H|T], N, Acc) -> count(H, T, N+1, Acc);
count({U, C}, [H|T], N, Acc) -> count(H, T, 1, [{U, C, N}|Acc]);
count({U, C}, [], N, Acc) -> [{U, C, N}|Acc].
transform(L) ->
transform(L, []).
transform([], Acc) -> Acc;
transform([{_, User, Category}|T], Acc) ->
transform(T, [{User, Category}|Acc]).
Edit:
The key point to determine which algorithm will be faster is a proportion of unique keys. If there is big dataset but with a small amount of unique {User, Category} the solution using maps will be faster. If it is the other way around, lists:sort/1 will be faster. In other words, the size of list vs. map matter.
I want to write a function to replace a specific atom with the given atom in an input list. But I want to do it using pattern matching and not using conditional statements. Any idea?
And also I want to write a function to return unique atoms in an expression.
e.g.
Input:
[a, b, c, a, b]
Output:
c
Input:
[b, b, b, r, t, y, y]
Output:
[t, r]
Assuming you want to replace all instances and keep the order of the list (works with all terms):
replace(Old, New, List) -> replace(Old, New, List, []).
replace(_Old, _New, [], Acc) -> lists:reverse(Acc);
replace(Old, New, [Old|List], Acc) -> replace(Old, New, List, [New|Acc]);
replace(Old, New, [Other|List], Acc) -> replace(Old, New, List, [Other|Acc]).
For the unique elements filter, you need to keep a state of which elements you have looked at already.
It would be really awkward to implement such a function using only pattern matching in the function headers and you would not really gain anything (performance) from it. The awkwardness would come from having to loop through both the list in question and the list(s) keeping your state of already parsed elements. You would also loose a lot of readability.
I would recommend going for something simpler (works with all terms, not just atoms):
unique(List) -> unique(List, []).
unique([], Counts) ->
lists:foldl(fun({E, 1}, Acc) -> [E|Acc];
(_, Acc) -> Acc
end, [], Counts);
unique([E|List], Counts) ->
unique(List, count(E, Counts).
count(E, []) -> [{E, 1}];
count(E, [{E, N}|Rest]) -> [{E, N + 1}|Rest];
count(E, [{X, N}|Rest]) -> [{X, N}|count(E, Rest)].
One way I'm looking for solving your first question would be to use guards, instead of if statements. Using only pattern matching doesn't seem possible (or desirable, even if you can do it).
So, for instance, you could do something like:
my_replace([H|T], ToReplace, Replacement, Accum) when H == ToReplace ->
my_replace(T, ToReplace, Replacement, [Replacement|Accum]);
my_replace([H|T], ToReplace, Replacement, Accum) ->
my_replace(T, ToReplace, Replacement, [H|Accum]);
my_replace([], ToReplace, Replacement, Accum) ->
lists:reverse(Accum).
EDIT: Edited for simplicity and style, thanks for the comments. :)
For the second part of your question, what do you consider an "expression"?
EDIT: Nevermind that, usort doesn't completely remove duplicates, sorry.
Is it possible to write a faster equivalent to this function?
prepend(X, Tuple) ->
list_to_tuple([X | tuple_to_list(Tuple)]).
It looks to me like that sort of thing is discouraged. If you want a list, use one.
Getting Started with Erlang:
Tuples have a fixed number of things in them.
If you have a finite number of possible tuple lengths, you could do this:
prepend(X, {}) -> {X};
prepend(X, {A}) -> {X, A};
prepend(X, {A, B}) -> {X, A, B};
prepend(X, {A, B, C}) -> {X, A, B, C}.
You can continue this pattern for as long as you need.
As prepending an element is the same as inserting it at position 1, you can use the built-in function erlang:insert_element/3:
> erlang:insert_element(1, {a, b}, z).
{z,a,b}
This function was added in Erlang/OTP R16A.