代码错误
一般做法是使用带有模式匹配的函数子句。它的工作原理与案例一样,并且被认为更具可读性。它修复了您在实施中遇到的一个错误:
首先你的代码可以用这种方式重写。
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 保密。