erlang处理通信(代码错误)

时间:2015-04-28 21:46:01

标签: process erlang spawn

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

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

  

=错误报告==== 2015年4月28日:: 23:01:25 ===
  过程中的错误< 0.296.0>退出值:{if_clause,[{test,odd,2,[{file," test.erl"},{line,25}]}}}

     

=错误报告==== 2015年4月28日:: 23:01:25 ===
  过程中的错误< 0.297.0>退出值:{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.

1 个答案:

答案 0 :(得分:4)

此代码存在许多问题。你编译了吗?

首先,一个简单的问题是:您的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]传递;它应该以{{1​​}}传递。我们说RestA1是列表Rest;当您将其作为[2,3,4,5]传递给下一个递归调用时,新调用中的[Rest]等同于[A|Rest],并且因为[[2,3,4,5] | []]是无意义的操作而发生错误。< / p>

[2,3,4,5] rem 2odd/2函数的另一个问题是它们完全使用even/2,因为它们可以使用函数子句。 {1}}通常不会用于惯用的Erlang代码中。这些函数的另一个问题是发送消息的子句不会进行递归调用来处理if中剩余的任何元素。所以不要这样:

if

你可以写下这个:

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来构建新列表。

另一个问题是你通过将原子O++[A]传递给[A|O]c来人工处理列表末尾的方法。更好的方法是制作odd/2even/2并完全退出odd/1

even/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 调用的结果,并且两个进程的打印顺序是不确定的,因为它们是并发的。