5

我有两个列表: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 个函数的列表。我究竟做错了什么?

4

3 回答 3

10

这里发生的事情是,如果我们展开这张地图,count(L, 0)首先会调用它,然后将产生的乐趣传递给lists:map. 当结果 fun 与 的每个成员进行映射E并传递给匿名函数时,大多数元素的返回值是调用的结果count(Tail,Counter),它返回一个函数。

这是您的函数的重写版本。最重要的是

  1. 我修复了基本情况,否则,当将空集传递给时,您可能会遇到匹配错误count(),更重要的是,
  2. 在您的闭包中,为了确保正确的递归,您需要调用的返回值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]
于 2013-01-17T04:30:47.910 回答
0

让我们看看你的函数 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)].

或者,如果您对 map 功能有偏好:

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"])。**异常错误:函数mymodule中的错误函数4:'-numberOfOccurences/2-lc$^0/1-0-'/2(/home/tony/Projects/temp/src/mymodule.erl,第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 被使用。

发生次数:计数([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> 
于 2019-10-23T05:46:50.477 回答
0

作为记录,您不需要闭包来定义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].
于 2021-08-11T19:38:22.387 回答