Erlang程序示例(2):高阶函数 fun

本文深入探讨了Erlang中的高阶函数概念,包括map、filter等函数的应用实例,并介绍了如何利用高阶函数进行复杂的数据处理任务,如解析算法的设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


2、高阶函数 fun
 
2.1 示例 1 :映射 map 

map(F, [H|T]) -> [F(H)|map(F, T)];
map(F, [])    -> [].

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

2.2 示例 2 :循环 foreach

foreach(F, [H|T]) ->
    F(H),
    foreach(F, T);
foreach(F, []) ->
    ok.

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

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

2.3 高阶函数语法

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

语法1:同一模块中,把函数作为参数值传递

F = fun FunctionName/Arity

语法2:不同模块中,定义函数

F = {Module, FunctionName}

创建函数的不同方式:

-module(fun_test).
-export([t1/0, t2/0, t3/0, t4/0, double/1]).
-import(lists, [map/2]).

t1() -> map(fun(X) -> 2 * X end, [1,2,3,4,5]).

t2() -> map(fun double/1, [1,2,3,4,5]).

t3() -> map({?MODULE, double}, [1,2,3,4,5]).

double(X) -> X * 2.

函数求值的语法:

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

f(F, Args) when is_function(F) ->
   apply(F, Args);
f(N, _) when is_integer(N) ->
   N.

高阶函数是独特类型。有内建函数对其管理:

erlang:fun_info/1,2 查看fun的信息
erlang:fun_to_list/1 返回fun的文本表达式

erlang:check_process_code/2 如果进程内的funs依赖模块早期版本,返回true


2.4 高阶函数内部对变量的约束

(1) 出现在fun头部的变量,是未约束值的("fresh" variable);
(2) 变量定义在fun之前,并且出现在函数调用中,或者出现在fun的guard测试中的,其值约束于fun的外部;
(3) fun中的变量不可导出。

print_list(File, List) ->
    {ok, Stream} = file:open(File, write),
    foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List),
    file:close(Stream).

X是未经约束的新变量,Stream源于file:open。

把变量导入fun时,要受限制:有些模式匹配的操作,要放入guard表达式,并且不可写入fun的头部。

这是正确的:

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

下面是错误的:

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

2.5 高阶函数和模块lists

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. 

2.5.1 map

map(F, [H|T]) -> [F(H)|map(F, T)];
map(F, [])    -> [].

> Double = fun(X) -> 2 * X end.
#Fun<erl_eval.6.72228031>

> lists:map(Double, [1,2,3,4,5]).
[2,4,6,8,10]

2.5.2 any

any(Pred, [H|T]) ->
    case Pred(H) of
        true  ->  true;
        false ->  any(Pred, T)
    end;

any(Pred, []) ->
    false.

> 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

2.5.3 all

all(Pred, [H|T]) ->
    case Pred(H) of
        true  ->  all(Pred, T);
        false ->  false
    end;

all(Pred, []) ->
    true.

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

2.5.4 foreach

foreach(F, [H|T]) ->
    F(H),
    foreach(F, T);
foreach(F, []) ->
    ok.

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

2.5.5 foldl

foldl(F, Accu, [Hd|Tail]) ->
    foldl(F, F(Hd, Accu), Tail);

foldl(F, Accu, []) -> Accu.

> 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

2.5.6 mapfoldl

mapfoldl(F, Accu0, [Hd|Tail]) ->
    {R,Accu1} = F(Hd, Accu0),
    {Rs,Accu2} = mapfoldl(F, Accu1, Tail),
    {[R|Rs], Accu2};
mapfoldl(F, Accu, []) -> {[], Accu}.

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

2.5.7 filter

filter(F, [H|T]) ->
    case F(H) of
        true  -> [H|filter(F, T)];
        false -> filter(F, T)
    end;

filter(F, []) -> [].

> lists:filter(Big, [500,12,2,45,6,7]).
[500,12,45]

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

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

2.5.8 takewhile

takewhile(Pred, [H|T]) ->
    case Pred(H) of
        true  -> [H|takewhile(Pred, T)];
        false -> []
    end;

takewhile(Pred, []) ->
    [].

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

2.5.9 dropwhile

dropwhile(Pred, [H|T]) ->
    case Pred(H) of
        true  -> dropwhile(Pred, T);
        false -> [H|T]
    end;

dropwhile(Pred, []) ->
    [].

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

2.5.10 splitwith

splitwith(Pred, L) ->
    splitwith(Pred, L, []).

splitwith(Pred, [H|T], L) ->
    case Pred(H) of 
        true  -> splitwith(Pred, T, [H|L]);
        false -> {reverse(L), [H|T]}
    end;

splitwith(Pred, [], L) ->
    {reverse(L), []}.

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

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

2.6.1 简单的高阶函数

> 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

2.6.2 无限列表

-module(lazy).
-export([ints_from/1]).
ints_from(N) ->
    fun() ->
            [N|ints_from(N+1)]
    end.

> 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

2.6.3 解析算法(Parsing)

解析函数:

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

Toke是解析对象(符号)的列表。Tree是解析树,Toks1是树的尾节点。解析失败返回fail。

要解析的语法:

(a | b) & (c | d)

pconst(X) ->
    fun (T) ->
       case T of
           [X|T1] -> {ok, {const, X}, T1};
           _      -> fail
       end
    end.

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: 

pand(P1, P2) ->
    fun (T) ->
        case P1(T) of
            {ok, R1, T1} ->
                case P2(T1) of
                    {ok, R2, T2} ->
                        {ok, {'and', R1, R2}};
                    fail ->
                        fail
                end;
            fail ->
                fail
        end
    end.

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. 

por(P1, P2) ->
    fun (T) ->
        case P1(T) of
            {ok, R, T1} -> 
                {ok, {'or',1,R}, T1};
            fail -> 
                case P2(T) of
                    {ok, R1, T1} ->
                        {ok, {'or',2,R1}, T1};
                    fail ->
                        fail
                end
        end
    end.

The original problem was to parse the grammar (a | b) & (c | d). The following code addresses this problem: 

grammar() ->
    pand(
         por(pconst(a), pconst(b)),
         por(pconst(c), pconst(d))).

The following code adds a parser interface to the grammar: 

parse(List) ->
    (grammar())(List).

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值