基本上我有一个包含Value和ID列表的结构。 我想要做的是映射ID列表并向它们发送消息,但是当我第一次初始化Ids列表时,我把变量" empty_set&#34 ;.(也许我应该将它重命名为empty_list:P )。
问题是每当我调用map函数时,我想先检查列表是否为" empty_set"如果没有,那么在其中使用map函数。这是代码:
{From, set_value, V} ->
if ViewerSet /= empty_set -> set_viewer_values(V, ViewerSet)
end,
looper(V, ViewerSet)
这是一个名为的函数:
set_viewer_values(Value, ViewerSet) ->
if ViewerSet /= empty_set ->
lists:map(fun(ViewerPid) ->
ViewerPid ! {self(), set_value, Value} end, ViewerSet)
end.
这就是我启动这个过程的方式:
process() ->
C = spawn(fun() -> looper(no_value, empty_set) end),
{ok, C}.
问题在于,当我运行它时,我收到此错误:
=ERROR REPORT==== 2-Nov-2014::15:03:07 ===
Error in process <0.367.0> with exit value: {function_clause,[{lists,map,
[#Fun<sheet.2.12938396>,empty_set],[{file,"lists.erl"},{line,1223}]},{lists,map,2,
[{file,"lists.erl"},{line,1224}]},{sheet,cell_loop,2,[{file,"sheet.erl"},{line,93}]}]}
据我所知,尽管if表达式我必须检查列表是否为空,但仍然会尝试映射它。
那么表达方式我做错了什么?
由于
答案 0 :(得分:2)
模式匹配。如果您需要检查一个警卫或if
或cond
中的空列表,几乎可以肯定您在思考Erlang方面存在结构性问题。
这几乎总会出现在令人困惑的代码和奇怪的边缘情况中,这会让你问自己像#34;我如何检查空列表?&#34;没有意识到你真正要问的是&#34;如何检查空列表作为程序条件?&#34;这是理智的函数式编程的祸根。
修改:可以按顺序进行更多解释和示例
无论你想要注入模式匹配,你都可以使用像case这样的东西,或者你可以将你正在做的事情分解成一个单独的函数。通常你会发现,你有一个语义含糊不清的地方,一方面事情过于紧密(你在receive
内接收消息以外的工作)太松散了另一方面(在调用函数之前,你正在进行大量的任意程序检查,当真正匹配参数是自然的解决方案时)。
looper(V, ViewerSet) ->
receive
{From, set_value, V} ->
set_viewer_values(V, ViewerSet),
looper(V, ViewerSet);
% OtherStuff ->
% whatever else looper/2 does...
end.
set_viewer_values(V, []) -> set_default_values(V);
set_viewer_values(V, ViewerSet) ->
% ... whatever the normal function definition is...
无论你从收到的地方派遣到哪里,都应该做什么工作,这也是你想要做的匹配的地方。因为这是函数调用,所以匹配在这里是一个很好的拟合并简化你的代码。
如果你想在looper/2
中匹配,这肯定是可能的。当你收到空名单时,我不知道你想做什么,所以我会做点什么,但你可以随心所欲:
looper(V, []) -> looper(V, default_set());
looper(V, ViewerSet) ->
% As before, or whatever makes sense.
你甚至可以决定当你有一个空集时,你需要以完全不同的方式操作:
full_looper(V, []) -> empty_looper(V);
full_looper(V, ViewerSet) ->
receive
{new_set, Set} ->
looper(V, Set);
{From, set_value, V} ->
set_viewer_values(V, ViewerSet),
looper(V, ViewerSet)
end.
empty_looper(V) ->
receive
{new_set, Set} ->
full_looper(V, Set);
{From, set_value, V} ->
set_viewer_values(V, default_set()),
empty_looper(V)
end.
我的观点是,有许多方法可以处理空集的情况,而不需要进行任意的程序检查,一旦你知道了解决方法,所有这些方法都会更容易阅读(直到你习惯这样做)但是,它感觉很奇怪)。作为旁注,最后一个示例实际上是创建一个有限状态机 - 并且已经有一个OTP模块可以使创建FSM变得非常容易。 (它们也很容易在Erlang中手写,但使用gen_fsm
模块更容易。)
答案 1 :(得分:0)
在if
两个表达式上,如果ViewerSet
为empty_set
,会发生什么?处理这种情况没有后卫。
if
表达式不是您在其他语言中看到的典型if
表达式。从我的经验来看,他们大多是避免的,并且有充分的理由:(如已经提到的另一个答案)模式匹配可用于检查相等性和其他比较操作(通过警卫)。
以下摘自here:
如果没有保护序列为真,则会发生
if_clause
运行时错误。如有必要,可以在最后一个分支中使用保护表达式true
,因为该保护序列始终为真。示例:
is_greater_than(X, Y) -> if X>Y -> true; true -> % works as an 'else' branch false end
所以if
表达式最终成为一种case
但是以布尔值作为它们的子句,它们往往会引入比清晰度更多的混淆。有些人甚至避免any usage of if
expression。
我的建议是,每当你看到自己使用if
表达式时,问问自己如何使用模式匹配替换它,使用case
或作为函数子句的一部分。
答案 2 :(得分:0)
如果您在变量ViewerSet
中有一个ID列表,只需使用空列表初始化它:[]
。
然后,当您收到消息{From,set_value,V}时,您可以使用lists:foreach/2
或使用列表理解为列表中的每个元素执行一个函数(即使它是空的):
{From, set_value, V} ->
lists:foreach(fun(ViewerPid) -> ViewerPid ! {self(), set_value, Value} end, ViewerSet),
looper(V, ViewerSet);
...
或
{From, set_value, V} ->
[fun(ViewerPid) -> ViewerPid ! {self(), set_value, Value} end || ViewerPid <- ViewerSet],
looper(V, ViewerSet);
...
答案 3 :(得分:0)
根据您的代码,这是您应该得到的:
(shell@a)8> Val.
myatom
(shell@a)9> if Val /= myatom -> lists:map(fun(X) -> io:format("~p",[X]) end, Val) end.
** exception error: no true branch found when evaluating an if expression
(shell@a)10>
所以似乎问题存在于其他地方。