From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- system/doc/programming_examples/funs.xmlsrc | 515 ++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 system/doc/programming_examples/funs.xmlsrc (limited to 'system/doc/programming_examples/funs.xmlsrc') diff --git a/system/doc/programming_examples/funs.xmlsrc b/system/doc/programming_examples/funs.xmlsrc new file mode 100644 index 0000000000..92f99cf6d3 --- /dev/null +++ b/system/doc/programming_examples/funs.xmlsrc @@ -0,0 +1,515 @@ + + + + +
+ + 20032009 + Ericsson AB. All Rights Reserved. + + + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + + + Funs + + + + + funs.xml +
+ +
+ Example 1 - map +

If we want to double every element in a list, we could write a + function named double:

+ +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, which adds one to every + element in a list:

+ +add_one([H|T]) -> [H+1|add_one(T)]; +add_one([]) -> []. +

These functions, double and add_one, have a very + similar structure. We can exploit this fact and write a function + map which expresses this similarity:

+ +

We can now express the functions double and + add_one in terms of map as follows:

+ +double(L) -> map(fun(X) -> 2*X end, L). +add_one(L) -> map(fun(X) -> 1 + X end, L). +

map(F, List) is a function which takes a function + F and a list L as arguments and returns the new + list which is obtained by applying F to each of + the elements in 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:

+ + write one function which represents the common features of + these functions + parameterize the difference in terms of functions which + are passed as arguments to the common function. + +
+ +
+ Example 2 - foreach +

This example illustrates procedural abstraction. Initially, we + show the following two examples written as conventional + functions:

+ + all elements of a list are printed onto a stream + a message is broadcast to a list of processes. + + +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 foreach expresses this similarity:

+ +

Using foreach, print_list becomes:

+ +foreach(fun(H) -> io:format(S, "~p~n",[H]) end, L) +

broadcast becomes:

+ +foreach(fun(Pid) -> Pid ! M end, L) +

foreach is evaluated for its side-effect and not its + value. foreach(Fun ,L) calls Fun(X) for each + element X in L and the processing occurs in + the order in which the elements were defined in L. + map does not define the order in which its elements are + processed.

+
+ +
+ The Syntax of Funs +

Funs are written with the syntax:

+ +F = fun (Arg1, Arg2, ... ArgN) -> + ... + end +

This creates an anonymous function of N arguments and + binds it to the variable F.

+

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 with the syntax:

+ +F(Arg1, Arg2, ..., Argn) +

To check whether a term is a fun, use the test + is_function/1 in a guard. Example:

+ +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.

+
+
+ +
+ Variable Bindings Within a Fun +

The scope rules for variables which occur in funs are as + follows:

+ + All variables which occur in the head of a fun are assumed + to be "fresh" variables. + Variables which are defined before the fun, and which + occur in function calls or guard tests within the fun, have + the values they had outside the fun. + No variables may be exported from a fun. + +

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 X which is defined in + the head of the fun is a new variable. The value of the variable + Stream which is used within within the fun gets its value + from the file:open line.

+

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, File is used as the new variable + instead of X. This is rather silly since code in the body + of the fun cannot refer to the variable File which is + defined outside the fun. Compiling this example will yield + the diagnostic:

+ +./FileName.erl:Line: Warning: variable 'File' + shadowed in 'lambda head' +

This reminds us that the variable File which is defined + inside the fun collides with the variable File which is + defined outside the fun.

+

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 to be evaluated when the value of + its argument is Y:

+ +f(...) -> + Y = ... + map(fun(X) when X == Y -> + ; + (_) -> + ... + end, ...) + ... +

instead of

+ +f(...) -> + Y = ... + map(fun(Y) -> + ; + (_) -> + ... + end, ...) + ... +
+ +
+ Funs and the Module Lists +

The following examples show a dialogue with the Erlang shell. + All the higher order functions discussed are exported from + the module lists.

+ +
+ map + +

map takes a function of one argument and a list of + terms. It returns the list obtained by applying the function + to every argument in the list.

+
+> 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 ]]>.

+
+ +
+ any + +

any takes a predicate P of one argument and a + list of terms. A predicate is a function which returns + true or false. any is true if there is a + term X in the list such that P(X) is true.

+

We define a predicate Big(X) which is true if + its argument is greater that 10.

+
+> 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
+
+ +
+ all + +

all has the same arguments as any. It is true + if the predicate applied to all elements in the list is true.

+
+> lists:all(Big, [1,2,3,4,12,6]).   
+false
+> lists:all(Big, [12,13,14,15]).       
+true
+
+ +
+ foreach + +

foreach takes a function of one argument and a list of + terms. The function is applied to each argument in the list. + foreach returns ok. It is used for its + side-effect only.

+
+> lists:foreach(fun(X) -> io:format("~w~n",[X]) end, [1,2,3,4]). 
+1
+2
+3
+4
+ok
+
+ +
+ foldl + +

foldl takes a function of two arguments, an + accumulator and a list. The function is called with two + arguments. The first argument is the successive elements in + the list, the second argument is the accumulator. The function + must return a new accumulator which is used the next time + the function is called.

+

If we have a list of lists L = ["I","like","Erlang"], + then we can sum the lengths of all the strings in L as + follows:

+
+> L = ["I","like","Erlang"].
+["I","like","Erlang"]
+10> lists:foldl(fun(X, Sum) -> length(X) + Sum end, 0, L).                    
+11
+

foldl works like a while loop in an imperative + language:

+ +L = ["I","like","Erlang"], +Sum = 0, +while( L != []){ + Sum += length(head(L)), + L = tail(L) +end +
+ +
+ mapfoldl + +

mapfoldl simultaneously maps and folds over a list. + The following example shows how to change all letters in + L to upper case and count them.

+

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}
+
+ +
+ filter + +

filter takes a predicate of one argument and a list + and returns all element in the list which satisfy + the predicate.

+
+> 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) to be + the difference between the lists L1 and L2. + This is the list of all elements in L1 which are not contained + in L2. This code can be written as follows:

+ +diff(L1, L2) -> + filter(fun(X) -> not member(X, L2) end, L1). +

The AND intersection of the list L1 and L2 is + also easily defined:

+ +intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2). +
+ +
+ takewhile + +

takewhile(P, L) takes elements X from a list + L as long as the predicate P(X) is true.

+
+> lists:takewhile(Big, [200,500,45,5,3,45,6]).  
+[200,500,45]
+
+ +
+ dropwhile + +

dropwhile is the complement of takewhile.

+
+> lists:dropwhile(Big, [200,500,45,5,3,45,6]).
+[5,3,45,6]
+
+ +
+ splitwith + +

splitwith(P, L) splits the list L into the two + sub-lists {L1, L2}, where L = takewhile(P, L) + and L2 = dropwhile(P, L).

+
+> lists:splitwith(Big, [200,500,45,5,3,45,6]).
+{[200,500,45],[5,3,45,6]}
+
+
+ +
+ Funs Which Return Funs +

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.

+ +
+ Simple Higher Order Functions +

Adder(X) is a function which, given X, returns + a new function G such that G(K) returns + K + X.

+
+> 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
+
+ +
+ Infinite Lists +

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".

+
+ +
+ Parsing +

The following examples show parsers of the following type:

+
+Parser(Toks) -> {ok, Tree, Toks1} | fail
+

Toks is the list of tokens to be parsed. A successful + parse returns {ok, Tree, Toks1}, where Tree is a + parse tree and Toks1 is a tail of Tree which + contains symbols encountered after the structure which was + correctly parsed. Otherwise fail is returned.

+

The example which follows illustrates a simple, functional + parser which parses the grammar:

+
+(a | b) & (c | d)
+

The following code defines a function pconst(X) in + the module funparse, which returns a fun which parses a + list of tokens.

+ +

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 pand + and por which combine primitive parsers to produce more + complex parsers. Firstly pand:

+ +

Given a parser P1 for grammar G1, and a parser + P2 for grammar G2, pand(P1, P2) returns a + parser for the grammar which consists of sequences of tokens + which satisfy G1 followed by sequences of tokens which + satisfy G2.

+

por(P1, P2) returns a parser for the language + described by the grammar G1 or G2.

+ +

The original problem was to parse the grammar + . The following code addresses this + problem:

+ +

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
+
+
+
+ -- cgit v1.2.3