2015-04-28 46 views
0

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

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

=错误报告==== 28-APR-2015 :: 23:01:25 ===
错误的过程< 0.296。 0>退出值:{if_clause,[{test,odd,2,[{file,“test.erl”},{line,25}]}]}

= ERROR REPORT ==== 28- Apr-2015 :: 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. 

回答

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];它应该只是作为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]

还有一个问题是你通过传递原子codd/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()调用的结果,和来自这两个进程的打印顺序是不确定的,因为它们是并发的。

+0

伟大的职位。是否有任何意见扭转列表只是为了排序呢? –

+0

即使排序本身解决了这个问题,但我留下了相反的情况,并将其排序为两个不连续的步骤,因为我想让反转非常明显。这是因为这篇文章解释了在使用'[Head | Tail]构建列表时,如何最终得到一个与您真正想要的顺序相反的列表,并最终颠倒它是常见的。我在那里反过来提醒读者。 –

+0

这完全是超级。 我还有一个问题,我怎样才能让这些流程做好工作并在他们到达时接收消息。我已经看到了一些loop()实现,但是它们都表明这个过程正在等待。有没有办法实现通知? 非常感谢 – ErlangNewbie