在Erlang过度使用警卫?

时间:2010-04-12 22:02:58

标签: erlang

我有以下函数,它取一个像5这样的数字,并创建一个从1到该数字的所有数字的列表,所以create(5)。返回[1,2,3,4,5]。

我认为我过度使用了警卫,并且想知道是否有更好的方法来编写以下内容:

create(N) ->
    create(1, N).

create(N,M) when N =:= M ->
    [N];
create(N,M) when N < M ->
    [N] ++ create(N + 1, M).

3 个答案:

答案 0 :(得分:6)

N < M的守卫可能很有用。一般来说,你不需要保护平等;你可以使用模式匹配。

create(N) -> create(1, N).

create(M, M) -> [M];
create(N, M) when N < M -> [N | create(N + 1, M)].

你通常也想编写函数,因此它们是尾递归的,其中通常的习惯用法是写入头部然后在最后反转。

create(N) -> create(1, N, []).

create(M, M, Acc) -> lists:reverse([M | Acc]);
create(N, M, Acc) when N < M -> create(N + 1, M, [N | Acc]).

(当然,通过这个具体的例子,您可以选择以相反的顺序构建结果,而不是最多M,这将使lists:reverse调用变得不必要。)

如果create/2(或create/3)未导出,并且您在create/1上放置了适当的后卫,则额外的N < M后卫可能会过度。我通常只检查导出的函数并信任我自己的内部函数。

答案 1 :(得分:4)

create(N,N) -> [N];
create(N,M) -> [N|create(N + 1, M)]. % Don't use ++ to prefix a single element.

这不完全相同(您可以提供-5),但如果您提供有意义的输入,它的行为相同。无论如何,我都不会为额外的检查而烦恼,因为无论如何这个过程都会很快崩溃。

顺便说一句,你有一个代码的递归深度问题。这将解决它:

create(N) ->
    create(1, N, []).

create(N, N, Acc) -> [N|Acc];
create(N, M, Acc) -> create(N, M - 1, [M|Acc]).

答案 2 :(得分:1)

我真的不认为你有过度使用的警卫。有两种情况:

第一个是create / 2

第一个子句中的显式相等测试
create(N, M) when N =:= M -> [M];

有些人建议将其转换为使用模式匹配,如

create(N, N) -> [N];

在这种情况下,它没有区别,因为编译器在内部将模式匹配版本转换为您编写的版本。您可以安全地选择您认为最适合的版本。

在第二种情况下,您需要某种形式的完整性检查,以确定参数的值在您期望的范围内。在每个循环中执行是不必要的,我会将它移动到create / 1中的等效测试:

create(M) when M > 1 -> create(1, M).

如果你想使用累加器我个人会使用计数版本,因为它保存了最后反转列表。如果列表不长,我认为差异非常小,您可以选择最清楚的版本。无论如何,如果你觉得它很重要,以后很容易改变。