我是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.
答案 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}}传递。我们说Rest
是A
而1
是列表Rest
;当您将其作为[2,3,4,5]
传递给下一个递归调用时,新调用中的[Rest]
等同于[A|Rest]
,并且因为[[2,3,4,5] | []]
是无意义的操作而发生错误。< / p>
[2,3,4,5] rem 2
和odd/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/2
和even/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
调用的结果,并且两个进程的打印顺序是不确定的,因为它们是并发的。