Erlang (programming language)/Tutorials/Folding

From Citizendium
< Erlang (programming language)‎ | Tutorials
Revision as of 20:02, 2 August 2009 by imported>Eric Evers
Jump to navigation Jump to search

Fun with folding

Fold

Fold is a powerful tool, when you get to know it. lists:foldr(F,S,L) takes three arguments: F is some folding function, S is some starting value, and L is some List that needs folding. Let us do some simple folding. The following fold calculates the lenght of a list. Here the current list item, _C is ignored, and the accumulator, A, counts the number of elements in the list.

 1> lists:foldr( fun(_C,A)->A+1 end, 0, [1,2,3]).
 3

We could reverse a list with fold if we like.

 2> lists:foldr(fun(C,A)->A++[C] end, [], [a,b,c]). 
 [c,b,a]

Or to get fancy we could try a finite difference.

 3> lists:foldr(
   fun(C,{P,A})->{C,[P-C]++A} end, 
   {0,[]},   
   [1,4,9,16]).  
 {1,[3,5,7,-16]}

In example 3, we used a tuple to remember the previous value so we could use it for next difference. Fold is a function with a limited attention span. We might like to delete the last item because is has a non-intuitive relation to the source list.

Calculating a continuted calculation with fold seems natural

lists:foldr(fun(C,A)->(A+1)/C end, 1, lists:seq(1,1000)).  
1.718281828459045

The answer appears to be the value e-1, which we can verify with:

math:exp(1) - 1. 
1.718281828459045

Unfold

The opposite of fold it the unfold. Unfold takes a seed and builds a list or larger structure, perhaps an infinite stream.

 %============================================================
 
 -module(fun_utils).
 -compile(export_all).
 
 % erlang unfold code thanks to Debasish Ghosh
 
 unfold(Seed, Predicate, Transformer) when is_integer(Seed) ->
   unfold(Seed, Predicate, Transformer, 1).
 
 unfold(Seed, Predicate, Transformer, Incrementor) ->
   unfold(Seed, Predicate, Transformer, Incrementor, []).
 
 unfold(Seed, Predicate, Transformer, Incrementor, Acc) ->
   case Predicate(Seed) of
       true -> unfold(Incrementor(Seed),
                      Predicate,
                      Transformer,
                      Incrementor,
                      [Transformer(Seed)|Acc]);
       false -> lists:reverse(Acc)
   end.
 
 % This front-end makes zip behave like the native lists:zip(L1,L2)
 % in the popular erlang lib. 
 
 zip(L1,L2) ->
 	lists:map(fun(L)->list_to_tuple(L) end, zip([L1,L2])).
 
 % a pure 100% list of lists based zip 
 
 zip(Ls) ->
   unfold(Ls,
          fun(X) -> length(hd(X)) > 0 end,
          fun(Lists) -> [hd(List) || List <- Lists] end,
          fun(Lists) -> [List -- [hd(List)] || List <- Lists] end).
 
 %===================================================================
 % Run it
 % 24> c(fun_utils).
 % ok
 
 % 25> fun_utils:zip([1,2,3],[4,5,6]).  
 % [{1,4},{2,5},{3,6}]