Erlang:累加器的串行实现

Erlang:累加器的串行实现,erlang,Erlang,我正在尝试创建一个方法,该方法接受一个关联的和可交换的运算符,以及一个值列表,然后通过对列表中的值应用一个运算符来返回答案 以下两个示例表示输入/输出应该是什么样子 例1 Input: sum(fun(A,B) -> A+B end, [2,6,7,10,12]). Output: 37 例2 Input: sum(fun (A,B) -> A++B end , ["C", "D", "E"]). Output: "CDE" 这是我目前正在使用的代码 -module(test

我正在尝试创建一个方法,该方法接受一个关联的和可交换的运算符,以及一个值列表,然后通过对列表中的值应用一个运算符来返回答案

以下两个示例表示输入/输出应该是什么样子

例1

Input: sum(fun(A,B) -> A+B end, [2,6,7,10,12]).

Output: 37
例2

Input: sum(fun (A,B) -> A++B end , ["C", "D", "E"]).

Output: "CDE"
这是我目前正在使用的代码

-module(tester).
-compile(export_all).

sum(Func, Data, Acc) ->
    lists:foldr(Func, Acc, Data).
这段代码产生了正确的结果,但是,有两个问题我正试图找出如何回答

(1) 为了使此代码正常工作,它需要在命令行语句的末尾包含一个空列表。换句话说,如果我输入上面的输入(如示例中所示),它将出错,因为我没有按以下方式编写:

12> tester:sum(fun(X, Acc) -> X+Acc end, [2,6,7,10,12], 0).
如果没有上面示例中的空列表,我将如何实现这一点并获得相同的结果

(2) 另外,如果没有list函数,或者以更串行的方式,代码将如何实现

如果没有上面示例中的空列表,我将如何实现这一点并获得相同的结果

假设列表中总是有一个元素(如果没有这个假设,您就无法真正做到这一点),您可以从列表中提取第一个元素,并将其作为初始累加器传递。您需要切换到
foldl
才能有效地执行此操作。(使用
foldr
时,基本上需要复制列表以删除最后一个元素。)

另外,如果没有list函数,或者以更串行的方式,代码将如何实现

下面是一个使用递归和模式匹配的简单实现:

sum2(Func, [X | Xs]) ->
  sum2(Func, Xs, X).

sum2(Func, [], Acc) ->
  Acc;
sum2(Func, [X | Xs], Acc) ->
  sum2(Func, Xs, Func(Acc, X)).
我们定义了函数的两个版本。第一种方法提取头部并将其用作初始累加器。第二个是arity 3,它基本上完成了
列表中的fold函数所做的工作

如果没有上面示例中的空列表,我将如何实现这一点并获得相同的结果

假设列表中总是有一个元素(如果没有这个假设,您就无法真正做到这一点),您可以从列表中提取第一个元素,并将其作为初始累加器传递。您需要切换到
foldl
才能有效地执行此操作。(使用
foldr
时,基本上需要复制列表以删除最后一个元素。)

另外,如果没有list函数,或者以更串行的方式,代码将如何实现

下面是一个使用递归和模式匹配的简单实现:

sum2(Func, [X | Xs]) ->
  sum2(Func, Xs, X).

sum2(Func, [], Acc) ->
  Acc;
sum2(Func, [X | Xs], Acc) ->
  sum2(Func, Xs, Func(Acc, X)).

我们定义了函数的两个版本。第一种方法提取头部并将其用作初始累加器。第二个是arity 3,基本上做了
列表中的折叠函数所做的事情。

在做了一段时间后,这就是我的解决方案。我已经对我所做的事情的总体想法发表了一些评论,但还有很多要说的

-module(erlang2).
-compile(export_all).
-export([reduce/2]).

reduce(Func, List) ->
    reduce(root, Func, List).

%When done send results to Parent
reduce(Parent, _, [A]) -> 
    %send to parent 
    Parent ! { self(), A}; 

   %I tried this at first to take care of one el in list, but it didn't work
   %length ([]) ->
   % Parent !   {self(), A};

%get contents of list, apply function and store in Parent
reduce(Parent, Func, List) ->
            { Left, Right } = lists:split(trunc(length(List)/2), List),
            Me = self(),
            %io:format("Splitting in two~n"),
            Pl = spawn(fun() -> reduce(Me, Func, Left) end),
            Pr = spawn(fun() -> reduce(Me, Func, Right) end),
            %merge results in parent and call Func on final left and right halves
            combine(Parent, Func,[Pl, Pr]).

%merge pl and pl and combine in parent
combine(Parent, Func, [Pl, Pr]) ->
    %wait for processes to complete (using receive) and then send to Parent
    receive
        { Pl, Sorted } -> combine(Parent, Func, Pr, Sorted);
        { Pr, Sorted } -> combine(Parent, Func, Pl, Sorted)
    end.

combine(Parent, Func, P, List) ->
    %wait and store in results and then call ! to send
    receive
        { P, Sorted } ->
            Results = Func(Sorted, List),
            case Parent of
                root ->
                    Results;
                %send results to parent
                _ -> Parent ! {self(), Results}
            end
    end.

经过一段时间的研究,这是我的解决方案。我已经对我所做的事情的总体想法发表了一些评论,但还有很多要说的

-module(erlang2).
-compile(export_all).
-export([reduce/2]).

reduce(Func, List) ->
    reduce(root, Func, List).

%When done send results to Parent
reduce(Parent, _, [A]) -> 
    %send to parent 
    Parent ! { self(), A}; 

   %I tried this at first to take care of one el in list, but it didn't work
   %length ([]) ->
   % Parent !   {self(), A};

%get contents of list, apply function and store in Parent
reduce(Parent, Func, List) ->
            { Left, Right } = lists:split(trunc(length(List)/2), List),
            Me = self(),
            %io:format("Splitting in two~n"),
            Pl = spawn(fun() -> reduce(Me, Func, Left) end),
            Pr = spawn(fun() -> reduce(Me, Func, Right) end),
            %merge results in parent and call Func on final left and right halves
            combine(Parent, Func,[Pl, Pr]).

%merge pl and pl and combine in parent
combine(Parent, Func, [Pl, Pr]) ->
    %wait for processes to complete (using receive) and then send to Parent
    receive
        { Pl, Sorted } -> combine(Parent, Func, Pr, Sorted);
        { Pr, Sorted } -> combine(Parent, Func, Pl, Sorted)
    end.

combine(Parent, Func, P, List) ->
    %wait and store in results and then call ! to send
    receive
        { P, Sorted } ->
            Results = Func(Sorted, List),
            case Parent of
                root ->
                    Results;
                %send results to parent
                _ -> Parent ! {self(), Results}
            end
    end.

非常感谢你的帮助。这很有效。下一步是试图找出如何实现同一累加器的并行版本。我正在考虑使用与合并排序相同的想法来完成此操作,然后生成单独的进程来对零件进行排序,然后合并它们,等等。您知道一种更简单的方法吗?您可以尝试在列表上使用
rpc:pmap/3
,然后合并其结果。不确定这是否是你要找的,但可能值得一查。非常感谢你的帮助。这很有效。下一步是试图找出如何实现同一累加器的并行版本。我正在考虑使用与合并排序相同的想法来完成此操作,然后生成单独的进程来对零件进行排序,然后合并它们,等等。您知道一种更简单的方法吗?您可以尝试在列表上使用
rpc:pmap/3
,然后合并其结果。不确定这是否是你要找的,但可能值得一查。