在Erlang中获取生成函数的结果

时间:2013-12-02 23:25:39

标签: list concurrency parallel-processing functional-programming erlang

我目前的目标是编写计算N个元素列表的Erlang代码,其中每个元素都是它的“索引”的因子(因此,对于N = 10,我想得到[1!,2!, 3!,...,10!])。更重要的是,我希望每个元素都能在一个单独的过程中计算(我知道它只是效率低下,但我希望能够实现它,并在以后将其效率与其他方法进行比较)。

在我的代码中,我想使用一个函数作为给定N的“循环”,对于N,N-1,N-2 ...产生一个计算阶乘(N)并将结果发送到一些“收集”功能,将收到的结果打包成一个列表。我知道我的概念可能过于复杂,所以希望代码可以解释一下:

messageFactorial(N, listPID) ->
    listPID ! factorial(N).      %% send calculated factorial to "collector".

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
nProcessesFactorialList(-1) ->
    ok;
nProcessesFactorialList(N) ->
    spawn(pFactorial, messageFactorial, [N, listPID]),   %%for each N spawn...
    nProcessesFactorialList(N-1).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
listPrepare(List) ->            %% "collector", for the last factorial returns
    receive                     %% a list of factorials (1! = 1).
        1 -> List;
        X ->
            listPrepare([X | List])
    end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
startProcessesFactorialList(N) ->
    register(listPID, spawn(pFactorial, listPrepare, [[]])),
    nProcessesFactorialList(N).

我想它会起作用,我的意思是listPrepare最终会返回一个阶乘列表。但问题是,我不知道如何获得该列表,如何获得它返回的内容?至于现在我的代码返回ok,因为这是nProcessesFactorialList在完成时返回的内容。我想最后将listPrepare的结果列表发送到nProcessesFactorialList,但是它还需要是一个注册过程,我不知道如何恢复该列表。

基本上,如何从注册进程运行listPrepare(这是我的阶乘列表)中得到结果?如果我的代码根本不对,我会问一个如何让它变得更好的建议。提前谢谢。

3 个答案:

答案 0 :(得分:9)

我的方式是如何做这种任务

-module(par_fact).

-export([calc/1]).

fact(X) -> fact(X, 1).

fact(0, R) -> R;
fact(X, R) when X > 0 -> fact(X-1, R*X).

calc(N) ->
    Self = self(),
    Pids = [ spawn_link(fun() -> Self ! {self(), {X, fact(X)}} end)
            || X <- lists:seq(1, N) ],
    [ receive {Pid, R} -> R end || Pid <- Pids ].

和结果:

> par_fact:calc(25).
[{1,1},
 {2,2},
 {3,6},
 {4,24},
 {5,120},
 {6,720},
 {7,5040},
 {8,40320},
 {9,362880},
 {10,3628800},
 {11,39916800},
 {12,479001600},
 {13,6227020800},
 {14,87178291200},
 {15,1307674368000},
 {16,20922789888000},
 {17,355687428096000},
 {18,6402373705728000},
 {19,121645100408832000},
 {20,2432902008176640000},
 {21,51090942171709440000},
 {22,1124000727777607680000},
 {23,25852016738884976640000},
 {24,620448401733239439360000},
 {25,15511210043330985984000000}]

答案 1 :(得分:0)

第一个问题是您的listPrepare进程对结果没有任何作用。最后尝试打印它。

第二个问题是你不要等待所有进程完成,而是等待发送1的进程,这是计算的最快因子。因此,在计算更复杂的内容之前肯定会收到此消息,并且您最终只会得到一些回复。

我在这里与许多流程的并行工作上回答了类似的问题:Create list across many processes in Erlang也许那个人会帮助你。

答案 2 :(得分:0)

我建议你这个解决方案:

-export([launch/1,fact/2]).

launch(N) ->
    launch(N,N).

%   launch(Current,Total)
%   when all processes are launched go to the result collect phase 
launch(-1,N) -> collect(N+1);
launch(I,N) ->
%   fact will be executed in a new process, so the normal way to get the answer is by message passing
%   need to give the current process pid to get the answer back from the spawned process 
    spawn(?MODULE,fact,[I,self()]),
%   loop until all processes are launched
    launch(I-1,N).

% simply send the result to Pid.
fact(N,Pid) -> Pid ! {N,fact_1(N,1)}.

fact_1(I,R) when I < 2 -> R;
fact_1(I,R) -> fact_1(I-1,R*I).

% init the collect phase with an empty result list
collect(N) -> collect(N,[]).

% collect(Remaining_result_to_collect,Result_list)
collect(0,L) -> L;
% accumulate the results in L and loop until all messages are received
collect(N,L) ->
    receive
        R -> collect(N-1,[R|L])
    end. 

但更简单(单一过程)的解决方案可能是:

1> F = fun(N) -> lists:foldl(fun(I,[{X,R}|Q]) -> [{I,R*I},{X,R}|Q] end, [{0,1}], lists:seq(1,N)) end. 
#Fun<erl_eval.6.80484245>
2> F(6).
[{6,720},{5,120},{4,24},{3,6},{2,2},{1,1},{0,1}]

<强> [编辑]

在具有多核,缓存和多任务底层系统的系统上,绝对不能保证执行顺序,在发送消息时也是如此。唯一的保证是在消息队列中,您知道您将根据消息接收的顺序分析消息。所以我同意德米特里的观点,你的停止条件不是100%有效。

此外,使用startProcessesFactorialList,您生成listPrepare,它有效地收集所有因子值(1除外!)然后在过程结束时忘记结果,我想这段代码片段并不完全是您用于测试的那个。