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