2

基本上我有一个结构,其中包括一个值和一个 ID 列表。我想要做的是映射 Id 列表并向他们发送消息,但是当我第一次初始化 Id 列表时,我放置了变量“empty_set”。(也许我应该将它重命名为 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 表达式我必须检查列表是否为空,但它仍会尝试映射它。

那么我在表达上做错了什么?

谢谢

4

4 回答 4

3

模式匹配。如果您需要检查守卫中的空列表,if或者cond几乎可以肯定您对 Erlang 的思考方式存在结构性问题。

这几乎总是会出现在令人困惑的代码和奇怪的边缘情况中,让你问自己诸如“如何检查空列表?”之类的问题。没有意识到您真正要问的是“如何检查空列表作为程序条件?” 这是健全的函数式编程的祸根。

编辑:更多的解释和一个例子可能是为了

无论您想在哪里注入模式匹配,您都可以使用 case 之类的东西,也可以将您正在做的任何事情分解成一个单独的函数。很多时候你会发现你有一个语义模糊,一方面事物耦合得太紧(你正在做的工作不是在 a 中接收消息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模块更容易。)

尝试使用 Case 检查列表何时为空而不是递归?

于 2014-11-02T15:02:03.070 回答
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> 

因此,问题似乎出在其他地方。

于 2014-11-22T01:41:20.827 回答
0

If you have a list of ids in the variable ViewerSet, simply initialize it with the empty list: [].

Then when you receive the message {From, set_value, V} you can execute a function for each element of the list (even if it is empty) using lists:foreach/2 or using list comprehension:

{From, set_value, V} ->
  lists:foreach(fun(ViewerPid) -> ViewerPid ! {self(), set_value, Value} end, ViewerSet),
  looper(V, ViewerSet);
...

or

{From, set_value, V} ->
  [fun(ViewerPid) -> ViewerPid ! {self(), set_value, Value} end || ViewerPid <- ViewerSet],
  looper(V, ViewerSet);
...
于 2014-11-02T20:08:17.043 回答
0

在这两个if表达式上,如果ViewerSetis会发生什么empty_set?没有警卫可以处理这种情况。

ififErlang 中的表达式不是您在其他语言中看到的典型表达式。根据我的少量经验,它们大多被避免,并且有充分的理由:(正如已经提到的另一个答案)模式匹配可用于检查相等性和其他比较操作(通过警卫)。

以下内容取自这里

如果没有保护序列为真,if_clause则会发生运行时错误。true如有必要,可以在最后一个分支中使用保护表达式,因为该保护序列始终为真。

例子:

is_greater_than(X, Y) ->
    if
        X>Y ->
            true;
        true -> % works as an 'else' branch
            false
    end

因此,if表达式最终成为一种case但以布尔值作为子句的形式,它们往往会带来更多的混乱而不是清晰。有些人甚至避免使用任何if表达方式

我的建议是,每次你看到自己使用if表达式时,问问自己如何用模式匹配替换它,无论是使用 acase还是作为函数子句的一部分。

于 2014-11-02T16:50:17.677 回答