2015-04-28 73 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