检查何时列表为空而不是递归的情况?

时间:2014-10-02 19:52:16

标签: erlang

我试着检查一个案例,如果列表是空的,而不是再次捕捉模式,这是正确的方式进入Erlang或我只是走错了路径和模式匹配是最好的方法如果列表已被清空则要捕获?

     calculate([Head|Tail], pi, x, y) ->
         ...calculations of Head being sent to a list...
         case Tail == [] of
            false ->
               calculate(Tail, pi, x, y)
         end.

或者我应该只是模式匹配计算列表是否为空?

3 个答案:

答案 0 :(得分:3)

代码错误

一般的做法是使用带有模式匹配的函数子句。它的工作原理就像它一样,它被认为更具可读性。它修复了您在实施中遇到的一个错误:

首先,你的代码可以用这种方式重写。

calculate([Head|Tail], pi, x, y) ->
  %% ... actual calculations ...
  calculate( Tail, pi, x, y);
calculate([], pi, x, y) ->
  %% you need to return something here, but you don't

正如您所看到的,其中一个子句不返回任何内容,这在Erlang中是不允许的(在编译期间失败)。你的实现完全一样。 case就像Erlang中的任何东西都必须返回一些值(因为它是函数中的lase语句,这个值将从函数返回)。由于case需要返回一些内容,因此需要匹配其中一个子句。在大多数情况下,由于Tail == []将返回false,因此不会出现问题。但最后一次递归调用,当Tail为空列表时,Tail == []将返回true,而case将与任何内容都不匹配。在Erlang中,这将导致(抛出或退出确切)case_clause错误。所以你的实现总是会失败。

要修复它,你需要确保在你的情况下总是有匹配的东西,比如这个

case Tail == [] of
  false ->
    calculate(Tail, pi, x, y)
  true ->
    %% return something 
end.

或者可以像这样写

case Tail of
  [] ->
    %% return something sane
  _ -> 
    calculate(Tail, pi, x, y)
end.

其中_将匹配任何内容,并且有点像else的其他语言。最后它可以用函数子句编写,就像我之前展示的那样,但是返回了这个合理的值。


修改

返回值

如果仔细观察我们的代码,我们现在只返回一个值;从最后一次递归调用(我称之为“理智”)的那一次。如果您想考虑所有递归调用的所有计算,您需要以某种方式累积它们。为此,我们将使用Acc变量

calculate([Head|Tail], pi, x, y, Acc) ->
  %% ... actual calculations with result assigned to Res variable ...
  NewAcc = [Res | Acc]
  calculate(Tail, pi, x, y, NewAcc);
calculate([], pi, x, y, Acc) ->
  Acc.

在每次递归调用中,我们将计算Res添加到累加器Acc,并将此更新列表发送到下一级递归。最后,当我们的输入列表为空(我们处理所有数据)时,我们只返回整个累加器。我们需要做的就是确保首次调用calculate时,使用空列表调用它Acc。这可以通过新的(有些旧的)函数

来完成
calculate(List, pi, x, y) ->
   calculate(List, pi, x, y, _Acc = []).

现在我们可以导出calculate/4并保持calculate/5私有。

答案 1 :(得分:3)

模式匹配。它是正确的事。

它也更有效率。它还可以防止你养成一种习惯,即在前面接受任何类型的变量,在你的功能中途发现并发现你所收到的东西甚至不是一个列表(哎呀!)。模式匹配(以及使用某些类型的警卫)也是Dialyzer检查成功类型的关键 - 这对您来说可能或不重要,但一旦您开始研究那些拥有客户的软件,肯定会这样做。

最重要的是,学习利用模式匹配教会你编写更小的函数。用一个可以完成所有事情的bajillion参数编写一个巨大的函数当然是可能的,甚至在许多其他语言中都很常见,但是模式匹配会向你说明为什么这一切都是一个糟糕的主意,一旦你开始写你的比赛案例。这将以我无法开始描述的方式帮助你;如果没有你最初欣赏它,你会看到你对程序的看法;它会减少你的嵌套条件(因为它们不会存在);它将教你停止在任何地方编写参数错误检查代码

答案 2 :(得分:0)

添加一个带有空列表的子句,如果不可能,则添加一个带有单个元素列表的子句:

func([H],P,X,Y) ->
     do_something(H,P,X,Y);
func([H|T],P,X,Y) ->
     do_something(H,P,X,Y),
     func(T,P,X,Y).

请注意,输入列表为空时会失败。

还要看一下你是否可以使用其中一个函数列表:map / 2或lists:foldl / 3或list comprehension ...