简体   繁体   English

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

[英]Getting result of a spawned function in Erlang

My objective at the moment is to write Erlang code calculating a list of N elements, where each element is a factorial of it's "index" (so, for N = 10 I would like to get [1!, 2!, 3!, ..., 10!]). 目前,我的目标是编写计算N个元素列表的Erlang代码,其中每个元素都是其“索引”的阶乘(因此,对于N = 10,我想得到[1 !, 2 !, 3 !, ...,10!])。 What's more, I would like every element to be calculated in a seperate process (I know it is simply inefficient, but I am expected to implement it and compare its efficiency with other methods later). 而且,我希望每个元素都在一个单独的过程中进行计算(我知道它只是效率低下,但我希望将其实现并将其效率与以后的其他方法进行比较)。

In my code, I wanted to use one function as a "loop" over given N, that for N, N-1, N-2... spawns a process which calculates factorial(N) and sends the result to some "collecting" function, which packs received results into a list. 在我的代码中,我想使用一个函数作为给定N上的“循环”,对于N,N-1,N-2 ...产生一个计算出factorial(N)并将结果发送到“收集”的过程。 ”功能,它将收到的结果打包到一个列表中。 I know my concept is probably overcomplicated, so hopefully the code will explain a bit more: 我知道我的概念可能过于复杂,因此希望代码能解释得更多:

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

I guess it shall work, by which I mean that listPrepare finally returns a list of factorials. 我想它将起作用,这意味着listPrepare最终将返回一个阶乘列表。 But the problem is, I do not know how to get that list, how to get what it returned? 但是问题是,我不知道如何获取该列表,如何获取它返回的内容? As for now my code returns ok, as this is what nProcessesFactorialList returns at its finish. 到目前为止,我的代码返回了ok,因为这是nProcessesFactorialList完成时返回的内容。 I thought about sending the List of results from listPrepare to nProcessesFactorialList in the end, but then it would also need to be a registered process, from which I wouldn't know how to recover that list. 我本来打算最后将结果列表从listPrepare发送到nProcessesFactorialList,但是那也需要一个已注册的过程,我不知道该如何恢复该列表。

So basically, how to get the result from a registered process running listPrepare (which is my list of factorials)? 因此,基本上,如何从运行listPrepare(这是我的阶乘列表)的注册进程中获取结果? If my code is not right at all, I would ask for a suggestion of how to get it better. 如果我的代码根本不正确,我会建议如何改善它。 Thanks in advance. 提前致谢。

My way how to do this sort of tasks is 我如何执行此类任务的方法是

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

and result: 结果:

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

The first problem is that your listPrepare process doesn't do anything with the result. 第一个问题是listPrepare进程对结果不执行任何操作。 Try to print it in the end. 尝试最后打印它。

The second problem is that you don't wait for all the processes to finish, but for process that sends 1 and this is the quickest factorial to calculate. 第二个问题是您不等待所有进程完成,而是等待发送1进程,这是计算最快的阶乘。 So this message will surely be received before the more complex will be calculated, and you'll end up with only a few responses. 因此,在计算更复杂的内容之前,肯定会收到此消息,并且最终只会得到一些响应。

I had answered a bit similar question on the parallel work with many processes here: Create list across many processes in Erlang Maybe that one will help you. 我在这里与许多进程的并行工作回答了一个类似的问题: 在Erlang中的许多进程之间创建列表也许这会对您有所帮助。

I propose you this solution: 我为您提出以下解决方案:

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

but a much more straight (single process) solution could be: 但更直接的(单一过程)解决方案可能是:

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

[edit] [编辑]

On a system with multicore, cache and an multitask underlying system, there is absolutly no guarantee on the order of execution, same thing on message sending. 在具有多核,缓存和多任务基础系统的系统上,绝对不能保证执行顺序,消息发送也要保证。 The only guarantee is in the message queue where you know that you will analyse the messages according to the order of message reception. 唯一的保证是在消息队列中,您知道您将根据消息接收的顺序来分析消息。 So I agree with Dmitry, your stop condition is not 100% effective. 所以我同意德米特里(Dmitry)的说法,您的停止条件并非100%有效。

In addition, using startProcessesFactorialList , you spawn listPrepare which collect effectively all the factorial values (except 1!) and then simply forget the result at the end of the process, I guess this code snippet is not exactly the one you use for testing. 另外,使用startProcessesFactorialList ,生成listPrepare ,它可以有效地收集所有阶乘值(除了1!),然后只是忘记了过程结束时的结果,我猜这个代码片段并不完全是您用于测试的代码片段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM