【问题标题】:How to check if a list is empty in Erlang?如何检查Erlang中的列表是否为空?
【发布时间】:2014-12-29 06:54:05
【问题描述】:

基本上,我有一个结构,其中包含一个值和一个 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 表达式,但它仍会尝试映射它。

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

谢谢

【问题讨论】:

    标签: erlang erlang-shell


    【解决方案1】:

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

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

    编辑:可能需要更多解释和示例

    无论你想在哪里注入模式匹配,你都可以使用 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 模块更容易。)

    试试Case to check when list is empty rather then recursion?

    【讨论】:

    • 那么你的意思是这样做? case ViewerSet of empty_list -&gt; looper(V, ViewerSet); [_] -&gt; set_viewer_values(V, ViewerSet)
    • 我无法使用 looper 进行模式匹配。我可以吗?
    • @sokras 模式匹配机会在 Erlang 中无处不在,你只需要练习就能看到它们。我在上面添加了两个示例,一个在 set_viewer_values/2 中,一个在 looper/2 本身中。
    【解决方案2】:

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

    Erlang 中的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 还是作为函数子句的一部分。

    【讨论】:

      【解决方案3】:

      如果变量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);
      ...
      

      【讨论】:

        【解决方案4】:

        根据您的代码,这是您应该得到的:

        (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> 
        

        看来问题出在其他地方。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-11
          • 1970-01-01
          • 1970-01-01
          • 2016-03-24
          • 1970-01-01
          • 1970-01-01
          • 2011-12-19
          相关资源
          最近更新 更多