如何在 Erlang 中使用闭包?
Posted
技术标签:
【中文标题】如何在 Erlang 中使用闭包?【英文标题】:How to use closures in Erlang? 【发布时间】:2012-12-31 13:36:56 【问题描述】:我有两个列表:L 和 E。我尝试编写一个函数,它返回另一个列表,其中包含 L 中 E 中元素的出现次数。
-module(mymodule).
-export([count/2]).
-export([numberOfOccurences/2]).
count([Head|Tail], Counter) ->
fun(Element) -> if
[Head|Tail] == [] -> Counter;
Element == Head -> count(Tail, Counter + 1);
Element /= Head -> count(Tail, Counter)
end
end.
numberOfOccurences(L, E) ->
lists:map(count(L, 0), E).
mymodule:numberOfOccurences[1,2,"abc",2,1,"abc",4,1,1], [1,2,3,"abc"])
应该返回 [4,2,0,2]
。但它返回一个包含 4 个函数的列表。我做错了什么?
【问题讨论】:
【参考方案1】:为了记录,您不需要闭包来定义count
。
在我的书中,有一个明确的count
函数并适当地插入它会更清楚。
编辑:我将列表参数放在第二位,以匹配 lists
模块 API。
count(X, [], Acc) -> Acc;
count(X, [X|T], Acc) -> count(T, X, Acc+1);
count(X, [_|T], Acc) -> count(T, X, Acc).
numberOfOccurences(L, E) ->
[count(X, L, 0) || X <- E].
【讨论】:
【参考方案2】:让我们看看你的函数 count/2。
count([Head|Tail], Counter) ->
fun(Element) -> if
[Head|Tail] == [] -> Counter;
Element == Head -> count(Tail, Counter + 1);
Element /= Head -> count(Tail, Counter)
end
end.
此函数包含作为函数定义的语句。这是最后一条语句,它成为返回值。所以调用:
lists:map(count(L, 0), E).
确实返回了一个函数列表。 查看 count 函数的定义,它确实对 count 进行了递归调用,并且如果它曾经被调用过,它实际上可能会起作用,但事实并非如此。
我们可以在你的程序末尾添加一条语句来调用所有的元素,方法是这样改变调用:
numberOfOccurences(L, E) ->
[F(E) || F <- lists:map(count(L, 0), E)].
或者,如果您对地图功能有偏好:
numberOfOccurences(L, E) ->
lists:map(fun(F) -> F(E) end, lists:map(count(L, 0), E)).
但是这些不运行。
mymodule:numberOfOccurences([1,2,"abc",2,1,"abc",4,1,1], [1,2,3,"abc"])。 ** 异常错误:坏函数 4 在函数 mymodule:'-numberOfOccurences/2-lc$^0/1-0-'/2 (/home/tony/Projects/temp/src/mymodule.erl, line 20)
3>
就风格而言,代码更容易推断是否传递了计数参数,而不是以这种方式使用闭包。有时闭包是必不可少的,例如在使用 spawn/1 时,但这不是其中一种情况。
分析问题我同意第一阶段是一张地图,但是我建议最好通过折叠来计算匹配元素。但是,我通常会用列表推导代替地图调用。我只是觉得它看起来更整洁。
所以这是我的解决方案:
-module occurances.
-export [count/2].
count(Data,KeyList) ->
[ lists:foldl(
fun(X,Count) when X =:= Key -> Count+1;
(_,Count) -> Count
end,
0,Data)
|| Key <- KeyList].
注意模式与 foldl 函数内的值 Key 匹配将导致影子变量警告。而是使用 X =:= Key。
出现次数:count([1,2,"abc",2,1,"abc",4,1,1], [1,2,3,"abc"])。 [4,2,0,2]
因此,在使代码实际工作之后,如何将其放入一个闭包中,以便例如可以调用 spawn/1。让我们编辑我们的工作代码模块来制作这样的闭包,并让闭包将结果写入标准 io 以便我们可以看到结果。
make_closure(Data,KeyList) ->
fun() ->
io:format("~p~n",[count(Data,KeyList)])
end.
8> F=occurances:make_closure([1,2,"abc",2,1,"abc",4,1,1], [1,2,3,"abc"]).
F=occurances:make_closure([1,2,"abc",2,1,"abc",4,1,1], [1,2,3,"abc"]).
#Fun<occurances.0.132489632>
9> spawn(F).
spawn(F).
[4,2,0,2]
<0.107.0>
10>
【讨论】:
【参考方案3】:这里发生的事情是,如果我们展开这张地图,count(L, 0)
将首先被调用,然后将产生的乐趣传递给lists:map
。当产生的 fun 与 E
的每个成员进行映射并传递给匿名函数时,大多数元素的返回值是调用 count(Tail,Counter)
的结果,它返回一个函数。
这是你的函数的重写版本。最重要的是
-
我修正了基本情况,否则,当空集传递给
count()
时,您可能会遇到匹配错误,更重要的是,
在你的闭包中,为了保证正确的递归,你需要调用count()
的返回值,所以我将count()
调用的结果存储到F
,然后使用传递的元素调用该函数。
所以这里是更新的模块:
-module(mymodule).
-export([count/2]).
-export([numberOfOccurences/2]).
count([],Counter) ->
fun(_) -> Counter end;
count([Head|Tail], Counter) ->
fun(Element) ->
if
Element == Head ->
F = count(Tail, Counter + 1),
F(Element);
Element /= Head ->
F = count(Tail, Counter),
F(Element)
end
end.
numberOfOccurences(L, E) ->
lists:map(count(L, 0), E).
结果:
> mymodule:numberOfOccurences([1,2,"abc",2,1,"abc",4,1,1], [1,2,3,"abc"]).
[4,2,0,2]
【讨论】:
以上是关于如何在 Erlang 中使用闭包?的主要内容,如果未能解决你的问题,请参考以下文章