If we want to double every element in a list, we could write a
function named
double([H|T]) -> [2*H|double(T)];
double([]) -> [].
This function obviously doubles the argument entered as input as follows:
> double([1,2,3,4]). [2,4,6,8]
We now add the function
add_one([H|T]) -> [H+1|add_one(T)];
add_one([]) -> [].
These functions,
We can now express the functions
double(L) -> map(fun(X) -> 2*X end, L).
add_one(L) -> map(fun(X) -> 1 + X end, L).
The process of abstracting out the common features of a number of different programs is called procedural abstraction. Procedural abstraction can be used in order to write several different functions which have a similar structure, but differ only in some minor detail. This is done as follows:
This example illustrates procedural abstraction. Initially, we show the following two examples written as conventional functions:
print_list(Stream, [H|T]) ->
io:format(Stream, "~p~n", [H]),
print_list(Stream, T);
print_list(Stream, []) ->
true.
broadcast(Msg, [Pid|Pids]) ->
Pid ! Msg,
broadcast(Msg, Pids);
broadcast(_, []) ->
true.
Both these functions have a very similar structure. They both iterate over a list doing something to each element in the list. The "something" has to be carried round as an extra argument to the function which does this.
The function
Using
foreach(fun(H) -> io:format(S, "~p~n",[H]) end, L)
foreach(fun(Pid) -> Pid ! M end, L)
Funs are written with the syntax:
F = fun (Arg1, Arg2, ... ArgN) ->
...
end
This creates an anonymous function of
If we have already written a function in the same module and wish to pass this function as an argument, we can use the following syntax:
F = fun FunctionName/Arity
With this form of function reference, the function which is referred to does not need to be exported from the module.
We can also refer to a function defined in a different module with the following syntax:
F = {Module, FunctionName}
In this case, the function must be exported from the module in question.
The follow program illustrates the different ways of creating funs:
We can evaluate the fun
F(Arg1, Arg2, ..., Argn)
To check whether a term is a fun, use the test
f(F, Args) when is_function(F) ->
apply(F, Args);
f(N, _) when is_integer(N) ->
N.
Funs are a distinct type. The BIFs erlang:fun_info/1,2 can be used to retrieve information about a fun, and the BIF erlang:fun_to_list/1 returns a textual representation of a fun. The check_process_code/2 BIF returns true if the process contains funs that depend on the old version of a module.
In OTP R5 and earlier releases, funs were represented using tuples.
The scope rules for variables which occur in funs are as follows:
The following examples illustrate these rules:
print_list(File, List) ->
{ok, Stream} = file:open(File, write),
foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List),
file:close(Stream).
In the above example, the variable
Since any variable which occurs in the head of a fun is considered a new variable it would be equally valid to write:
print_list(File, List) ->
{ok, Stream} = file:open(File, write),
foreach(fun(File) ->
io:format(Stream,"~p~n",[File])
end, List),
file:close(Stream).
In this example,
./FileName.erl:Line: Warning: variable 'File'
shadowed in 'lambda head'
This reminds us that the variable
The rules for importing variables into a fun has the consequence
that certain pattern matching operations have to be moved into
guard expressions and cannot be written in the head of the fun.
For example, we might write the following code if we intend
the first clause of
f(...) ->
Y = ...
map(fun(X) when X == Y ->
;
(_) ->
...
end, ...)
...
instead of
f(...) ->
Y = ...
map(fun(Y) ->
;
(_) ->
...
end, ...)
...
The following examples show a dialogue with the Erlang shell.
All the higher order functions discussed are exported from
the module
> Double = fun(X) -> 2 * X end. #Fun<erl_eval.6.72228031> > lists:map(Double, [1,2,3,4,5]). [2,4,6,8,10]
When a new fun is defined in the shell, the value of the Fun
is printed as
We define a predicate
> Big = fun(X) -> if X > 10 -> true; true -> false end end. #Fun<erl_eval.6.72228031> > lists:any(Big, [1,2,3,4]). false > lists:any(Big, [1,2,3,12,5]). true
> lists:all(Big, [1,2,3,4,12,6]). false > lists:all(Big, [12,13,14,15]). true
> lists:foreach(fun(X) -> io:format("~w~n",[X]) end, [1,2,3,4]). 1 2 3 4 ok
If we have a list of lists
> L = ["I","like","Erlang"]. ["I","like","Erlang"] 10> lists:foldl(fun(X, Sum) -> length(X) + Sum end, 0, L). 11
L = ["I","like","Erlang"],
Sum = 0,
while( L != []){
Sum += length(head(L)),
L = tail(L)
end
First upcase:
> Upcase = fun(X) when $a =< X, X =< $z -> X + $A - $a; (X) -> X end. #Fun<erl_eval.6.72228031> > Upcase_word = fun(X) -> lists:map(Upcase, X) end. #Fun<erl_eval.6.72228031> > Upcase_word("Erlang"). "ERLANG" > lists:map(Upcase_word, L). ["I","LIKE","ERLANG"]
Now we can do the fold and the map at the same time:
> lists:mapfoldl(fun(Word, Sum) -> {Upcase_word(Word), Sum + length(Word)} end, 0, L). {["I","LIKE","ERLANG"],11}
> lists:filter(Big, [500,12,2,45,6,7]). [500,12,45]
When we combine maps and filters we can write very succinct
code. For example, suppose we want to define a set difference
function. We want to define
diff(L1, L2) ->
filter(fun(X) -> not member(X, L2) end, L1).
The AND intersection of the list
intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).
> lists:takewhile(Big, [200,500,45,5,3,45,6]). [200,500,45]
> lists:dropwhile(Big, [200,500,45,5,3,45,6]). [5,3,45,6]
> lists:splitwith(Big, [200,500,45,5,3,45,6]). {[200,500,45],[5,3,45,6]}
So far, this section has only described functions which take funs as arguments. It is also possible to write more powerful functions which themselves return funs. The following examples illustrate these type of functions.
> Adder = fun(X) -> fun(Y) -> X + Y end end. #Fun<erl_eval.6.72228031> > Add6 = Adder(6). #Fun<erl_eval.6.72228031> > Add6(10). 16
The idea is to write something like:
-module(lazy).
-export([ints_from/1]).
ints_from(N) ->
fun() ->
[N|ints_from(N+1)]
end.
Then we can proceed as follows:
> XX = lazy:ints_from(1). #Fun<lazy.0.29874839> > XX(). [1|#Fun<lazy.0.29874839>] > hd(XX()). 1 > Y = tl(XX()). #Fun<lazy.0.29874839> > hd(Y()). 2
etc. - this is an example of "lazy embedding".
The following examples show parsers of the following type:
Parser(Toks) -> {ok, Tree, Toks1} | fail
The example which follows illustrates a simple, functional parser which parses the grammar:
(a | b) & (c | d)
The following code defines a function
This function can be used as follows:
> P1 = funparse:pconst(a). #Fun<funparse.0.22674075> > P1([a,b,c]). {ok,{const,a},[b,c]} > P1([x,y,z]). fail
Next, we define the two higher order functions
Given a parser
The original problem was to parse the grammar
The following code adds a parser interface to the grammar:
We can test this parser as follows:
> funparse:parse([a,c]). {ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} > funparse:parse([a,d]). {ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}} > funparse:parse([b,c]). {ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}} > funparse:parse([b,d]). {ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}} > funparse:parse([a,b]). fail