I am trying to get my head round some basic erlang functionality and I could do with some comments on the following.
I have the following erlang code that takes a list of tuples and returns a list minus an element if a key is found:
delete(Key, Database) ->
remove(Database, Key, []).
remove([], Key, Acc) ->
Acc;
remove([H|T], Key, Acc) ->
if
element(1, H) /= Key ->
[H| remove(T, Key, Acc)];
true ->
remove(T, Key, Acc)
end.
Is this a good way of doing this?
The if statement seems incorrect.
Also is my use of the accumulator Acc making this tail recursive?
No, your usage of Acc doesn't make it tail recursive. Your branch of if returns [H| remove(T, Key, Acc)] which is not tail call and this branch would be used most of time. To be more precise your usage of Acc is useless because it would be [] whole time, you don't change its value at all. Correct code should look like.
delete(Key, Database) ->
remove(Database, Key, []).
remove([], Key, Acc) ->
lists:reverse(Acc);
remove([H|T], Key, Acc) ->
if
element(1, H) /= Key ->
remove(T, Key, [H|Acc]);
true ->
remove(T, Key, Acc)
end.
But if your list members are always pairs I would prefer direct pattern match:
delete(Key, Database) ->
remove(Database, Key, []).
remove([], Key, Acc) ->
lists:reverse(Acc);
remove([{Key, _}|T], Key, Acc) ->
remove(T, Key, Acc);
% if it should delete only first occurrence then lists:reverse(Acc, T);
remove([H|T], Key, Acc) ->
remove(T, Key, [H|Acc]).
But I think this is example where can apply Myth: Tail-recursive functions are MUCH faster than recursive functions so I would use much simpler recursive version:
delete(Key, []) -> [];
delete(Key, [{Key, _}|T]) -> delete(Key, T);
% if it should delete only first occurrence then just T;
delete(Key, [H|T]) -> [H | delete(Key, T)].
As mentioned, there is a standard module function that already does this (proplists:delete). Shouldn't need to say more, but...
I'd tend to keep the original method name (delete), but have a local version including the accumulator as parameter. The context makes me think the order of the tuples in the "database" doesn't matter, so that a lists:reverse isn't necessary.
-module(foo).
-export([delete/2]).
delete(Key, Database) ->
delete(Key, Database, []).
delete(_Key, [], Acc) ->
Acc;
delete(Key, [{Key, _} | T], Acc) ->
delete(Key, T, Acc);
delete(Key, [Entry={_, _} | T], Acc) ->
delete(Key, T, [Entry | Acc]).
A couple things here:
tail-recursive; in general I think it is safer to stick with tail-recursion -- while there are optimizations for body recursive calls, you'd really need to do some performance measurement with realistic (for your application) data to make a comparison
note that we're not accepting any old list here; the Entry pattern match in delete/3 helps ensure this (depending on what this is for, you may or may not want this)
Related
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'm required to write my own tuple_to_list() function (yes, from the book) and came up with this in my erl file:
%% Our very own tuple_to_list function! %%
% First, the accumulator function
my_tuple_to_list_acc(T, L) -> [element(1, T) | L];
my_tuple_to_list_acc({}, L) -> L;
% Finally, the public face of the function
my_tuple_to_list(T) -> my_tuple_to_list_acc(T, []).
When I compile this, however, I get the following error in the shell:
28> c(lib_misc).
lib_misc.erl:34: head mismatch
lib_misc.erl:2: function my_tuple_to_list/1 undefined
error
I have no clue what "head mismatch" there is, and why is the function undefined (I've added it to the module export statement, though I doubt this has much to do with export statements)?
The other answer explains how to fix this, but not the reason. So: ; after a function definition clause means the next clause continues the definition, just like as for case and if branches. head mismatch means you have function clauses with different names and/or number of arguments in one definition. For the same reason, it is an error to have a clause ending with . followed by another clause with the same name and argument count.
Changing the order of the clauses is needed for a different reason, not because of the error. Clauses are always checked in order (again, same as for case and if) and your first clause already matches any two arguments. So the second would never be used.
Those errors mean that you didn't end definition of my_tuple_to_list_acc/2.
You should change order of first two code lines and add dot after them.
my_tuple_to_list_acc({}, L) -> L;
my_tuple_to_list_acc(T, L) -> [element(1, T) | L].
When you are interested in working tuple_to_list/1 implementation
1> T2L = fun (T) -> (fun F(_, 0, Acc) -> Acc; F(T, N, Acc) -> F(T, N-1, [element(N, T)|Acc]) end)(T, tuple_size(T), []) end.
#Fun<erl_eval.6.50752066>
2> T2L({}).
[]
3> T2L({a,b,c}).
[a,b,c]
Or in module
my_typle_to_list(_, 0, Acc) -> Acc;
my_typle_to_list(T, N, Acc) ->
my_typle_to_list(T, N-1, [element(N, T)|Acc]).
my_typle_to_list(T) ->
my_typle_to_list(T, tuple_size(T), []).
Note how I use decreasing index for tail recursive function.
I'm trying to process a sequence of items whereby the process step relies on some additional cumulative state from the prior items (ordering isn't important).
Essentially:
I have a Seq<'A>
I have a (Type * int) list referred to as the skip list
I have a process step 'A -> (Type * int) list -> 'B option
This takes the current skip list
The method in question essentially:
Seq<'A'> -> (Type * int) list -> (Type * int) list
So we take a bunch of input items and an initial skip list and produce a final skip list.
I've basically got the following so far:
sourceItems
|> Seq.map (fun srcItem -> (srcItem, outerSkip))
|> Seq.unfold (fun elem ->
match elem with
| SeqEmpty -> None
| SeqCons((srcItem, skip), tail) ->
match process(srcItem, skip) with
| Some targetItem -> Some((Some targetItem, skip), tail)
| None -> Some((None, skip), tail |> Seq.map (fun (i, skp) -> (i, (srcItem.GetType(), liD srcItem) :: skp))))
With SeqEmpty and SeqCons being active patterns:
let (|SeqEmpty|SeqCons|) (xs: 'a seq) =
if Seq.isEmpty xs then SeqEmpty
else SeqCons(Seq.head xs, Seq.skip 1 xs)
My process so far basically just starts off with the items and adds the initial skip to each, unfolds and maps the remaining seq to have the same item but with the new skip list.
I have a number of problems with this:
It's ugly and confusing as all hell
I'm sure it's less than performant
Ideally I'd like to avoid the need to map the items to include the initial skip list in the first place, but then I'm not sure how I'd get that into the unfold aside from mapping it into just the first element in the sequence.
Possible Alternative Solution
Based on a different approach taken in List processing with intermediate state (Mark's answer)
I've been able to use:
items
|> Seq.fold (fun (skip) srcItem ->
match process(srcItem, skip) with
| None -> (srcItem.GetType(), liD srcItem) :: skip
| Some tgtItem ->
skip
) outerSkip
Which aside from all the stuff needed when there is an item, appears to actually do the trick!
This is significantly simpler than the unfold approach, but I'm a little unclear on exactly how it's working.
I'm assuming that fun (skip) srcItem -> ... is essentially creating a function expecting an additional parameter that through the magic of something (partial application?) I'm able to provide to fold using outerSkip - is this right?
I ended up adopting the fold strategy as mentioned in the question.
The final code is:
let result =
items
|> Seq.fold (fun (skip, targetItems), srcItem ->
match process(srcItem, skip) with
| None -> ((srcItem.GetType(), getId srcItem) :: skip, targetItems)
| Some tgtItem -> (skip, tgtItem :: targetItems)) (outerSkip, [])
With result being a tuple ((Type * int) list, obj list) which is exactly what I wanted.
I'm then able to take action on the target items, and to just return the final skip list.
This is a huge improvement over the unfold method I was using previously.
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.
I have a list of items that I would like to "un-zip-flatten". Basically what that means is that if I have a list of items:
[a, b, c, d, e, f, g]
I want to turn it into a list of lists like the following:
[[a, d, g], [b, e], [c, f]]
So far my solution looks like this:
unzipflatten(NumberOfLists, List) ->
lists:map(fun(Start) ->
lists:map(fun(N) ->
lists:nth(N, List)
end,
lists:seq(Start, length(List), NumberOfLists))
end,
lists:seq(1, NumberOfLists)).
I'm pretty new to Erlang so I'm wondering if I've missed some standard library function that would do what I want, or if there's a more "Erlangish" way to do this, or if the performance of my above solution will stink.
I think this would be a more "Erlangish" method to do this. Basically you would create the list of lists that will be your result, and use two lists to manage those lists like a queue. The "Heads" list contains the lists that you will prepend to next, and the "Tails" list are the ones most recently prepended to. When Heads is empty you simply reverse Tails and use that as the new Heads. Before returning the result you will need to reverse all of the lists inside Tails and Heads and then append Heads as-is to the reversed Tails. Excuse the confusing variable names, I think coming up with several good names for breaking up lists in an Erlang program is the hardest part ;)
unzipflatten(NumberOfLists, List) when NumberOfLists > 0 ->
unzipflatten(List, lists:duplicate(NumberOfLists, []), []).
unzipflatten([], Heads, Tails) ->
[lists:reverse(L) || L <- lists:reverse(Tails, Heads)];
unzipflatten(L, [], Tails) ->
unzipflatten(L, lists:reverse(Tails), []);
unzipflatten([Elem | Rest], [Head | Tail], Tails) ->
unzipflatten(Rest, Tail, [[Elem | Head] | Tails]).
It's also possible to do the "unzip" phase in a non tail-recursive way to avoid the lists:reverse step, but that is a more complicated solution. Something like this:
unzipflatten(NumberOfLists, List) when NumberOfLists > 0 ->
unzipflatten({List, lists:duplicate(NumberOfLists, [])}).
unzipflatten({[], Heads}) ->
[lists:reverse(L) || L <- Heads];
unzipflatten({L, Heads}) ->
unzipflatten(unzipper({L, Heads})).
unzipper({[], Heads}) ->
{[], Heads};
unzipper({L, []}) ->
{L, []};
unzipper({[H | T], [Head | Tail]}) ->
{T1, Tail1} = unzipper({T, Tail}),
{T1, [[H | Head] | Tail1]}.
Yes, performance will stink (basic advice for using lists:nth: never call it several times with growing N!). Something like this should be better (not tested):
unzipflatten(NumberOfLists, List) ->
unzipflatten(NumberOfLists, List, array:new(NumberOfLists, {default, []}), 0).
unzipflatten(_, [], Lists, _) ->
lists:map(fun lists:reverse/1, array:to_list(Lists));
unzipflatten(NumberOfLists, [H | T], Lists, CurrentIndex) ->
NewLists = array:set(CurrentIndex, [H | array:get(CurrentIndex, Lists)], Lists),
unzipflatten(NumberOfLists, T, NewLists, (CurrentIndex + 1) rem NumberOfLists).