如何检查Erlang中的列表是否为空?

时间:2014-11-02 14:19:07

标签: erlang erlang-shell

基本上我有一个包含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表达式我必须检查列表是否为空,但仍然会尝试映射它。

那么表达方式我做错了什么?

由于

4 个答案:

答案 0 :(得分:2)

模式匹配。如果您需要检查一个警卫或ifcond中的空列表,几乎可以肯定您在思考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模块更容易。)

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

答案 1 :(得分:0)

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或作为函数子句的一部分。

答案 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> 

所以似乎问题存在于其他地方。