erlang进程通信(代码错误)

erlang processes communication (code error)

我是 Erlang 新手。在我的代码中,我尝试为我的 2 个进程中的每一个提供一个数字列表。进程 oid 应将其列表中的所有偶数发送到进程 eid,进程应将其进程中的所有奇数发送到 oid。当一个进程完成过滤自己的列表时,它开始读取从另一个进程收到的消息,将它们添加到输出列表,最后打印该列表。

当我 运行 代码时,我得到这些错误:

=ERROR REPORT==== 28-Apr-2015::23:01:25 ===
Error in process <0.296.0> with exit value: {if_clause,[{test,odd,2,[{file,"test.erl"},{line,25}]}]}

=ERROR REPORT==== 28-Apr-2015::23:01:25 ===
Error in process <0.297.0> with exit value: {if_clause,[{test,even,2,[{file,"test.erl"},{line,49}]}]}

这是我的代码:

-module(test).
-export([start/0]).
-export([odd/2]).
-export([even/2]).

start() -> 
    register(oid, spawn(test, odd, [[1,2,3,4,5],[]])),
    register(eid, spawn(test, even, [[6,7,8,9],[]])).
%---------------------------------------------
odd(c,O) ->
    receive
        done -> 
            lists:foreach(
                fun(X) -> io:fwrite("~p\n", X) end, 
                io:fwrite("~p\n", oid)
            );
        Num -> 
            O++[Num],
            odd(c,O)
    end;
odd([],O) ->
    eid ! done,
    odd(c,O);

odd([A|Rest],O) -> 
    if 
        A rem 2 =:= 0 ->
            eid ! A;
        A rem 2 =/= 0 ->
            O++[A],
            odd([Rest],O)
    end.
%--------------------------------------------
even(c,E)->
    receive
        done -> 
            lists:foreach(
                fun(X) -> io:fwrite("~p\n", X) end, 
                io:fwrite("~p\n", eid)
            );
        Num ->
            E++[Num],
            even(c,E)
    end;

even([],E) ->
    oid ! done,
    even(c,E);

even([A|Rest],E) -> 
    if 
        A rem 2 =/= 0 ->
            oid ! A;
        A rem 2 =:= 0 ->
            E++[A],
            even([Rest],E)
    end.
%---------------------------------------------
pri([H|T]) ->
    io:format("~p~n", [H]),
    pri(T);
pri([]) ->
    true.

这段代码有很多问题。你编译了吗?

首先,一个简单的问题:您的 io:fwrite/2 调用需要在列表中传递要打印的参数,而不是作为单独的术语,因此:

io:fwrite("~p\n", oid)

错了。应该是:

io:fwrite("~p\n", [oid])

但是无论如何打印一个常量 oid 没有什么意义。你应该去掉这段代码:

lists:foreach(
    fun(X) -> io:fwrite("~p\n", X) end, 
    io:fwrite("~p\n", oid)
);

(已损坏且无法编译)并改用 pri/1 函数(或 pri/2,如后所示)。

接下来,您将尝试向列表中添加元素,就好像列表是可变的一样。 Erlang 中的变量是不可变的。而不是这个:

O++[A],
odd([Rest],O)

你需要:

odd(c,O++[Num])

创建一个新列表以传递给下一次迭代,或者更好:

odd(c,[Num|O])

这比附加更有效,因为它只是向列表中添加一个新头。请注意,虽然这会向后构建列表,因此我们稍后需要将其反转。幸运的是,反转列表非常便宜。

接下来,您的 if 语句错误消息是由您递归传递 Rest 的方式引起的。当您拥有构造 [A|Rest] 时,Rest 变量 已经是一个列表 。无需将其作为 [Rest] 传递;它应该作为 Rest 传递。假设 A1Rest 是列表 [2,3,4,5];当您将它作为 [Rest] 传递给下一个递归调用时,新调用中的 [A|Rest] 等同于 [[2,3,4,5] | []] 并且会发生错误,因为 [2,3,4,5] rem 2 是一个无意义的操作。

odd/2even/2 函数的另一个问题是它们完全使用 if,因为它们可以使用函数子句。 if 在惯用的 Erlang 代码中不常使用。这些函数的另一个问题是发送消息的子句不会进行递归调用来处理 Rest 中剩余的任何元素。所以不是这个:

odd([A|Rest],O) -> 
    if 
        A rem 2 =:= 0 ->
            eid ! A;
        A rem 2 =/= 0 ->
            O++[A],
            odd([Rest],O)
    end.

你可以这样写:

odd([A|Rest],O) when A rem 2 =:= 0 ->
    eid ! A,
    odd(Rest,O);
odd([A|Rest],O) ->
    odd(Rest,[A|O]).

请注意,这避免了两次 rem 测试的需要,因为在第一个子句中未被守卫检测为偶数的任何数字自动为奇数,因此由第二个子句处理。另请注意,我们使用前置形式 [A|O] 而不是 O++[A] 来构建新列表。

另一个问题是您通过将原子 c 传递给 odd/2even/2 来处理列表末尾的人为方式。更好的方法是使 odd/1even/1 完全离开 c

odd(O) ->
    receive
        done ->
            L = lists:sort(lists:reverse(O)),
            pri(oid,L);
        Num ->
            odd([Num|O])
    end.

这种方法使用pri/2进行打印,传递给它的列表在这里被反转以取消通过前置构建它的效果,并排序以使其有序。 pri/2 函数如下所示:

pri(Id, [H|T]) ->
    io:format("~p: ~p~n", [Id, H]),
    pri(Id,T);
pri(_, []) ->
    true.

如果你运行整个事情,你会得到这样的东西:

2> test:start().
eid: 2
oid: 1
eid: 4
oid: 3
true
oid: 5
eid: 6
oid: 7
eid: 8
oid: 9

中间的 truetest:start() 调用的结果,两个进程的打印顺序是不确定的,因为它们是并发的。