我有以下函数,它取一个像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).
答案 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).
如果你想使用累加器我个人会使用计数版本,因为它保存了最后反转列表。如果列表不长,我认为差异非常小,您可以选择最清楚的版本。无论如何,如果你觉得它很重要,以后很容易改变。