Erlang 中过度使用守卫?

发布于 2024-08-29 03:47:13 字数 280 浏览 4 评论 0原文

我有以下函数,它接受 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).

I have the following function that takes a number like 5 and creates a list of all the numbers from 1 to that number so create(5). returns [1,2,3,4,5].

I have over used guards I think and was wondering if there is a better way to write the following:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不及他 2024-09-05 03:47:13

N 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]).

(当然,在这个具体示例中,您也可以按照相反的顺序构建结果,从 1 而不是到 M,这将使 lists:reverse 调用变得不必要。)

如果 create/2(或create/3)不会被导出,并且您在create/1上放置了适当的保护,额外的N M 后卫可能有点矫枉过正。我通常只检查导出的函数并信任我自己的内部函数。

The guard for N < M can be useful. In general, you don't need a guard for equality; you can use pattern-matching.

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

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

You also generally want to write functions so they are tail-recursive, in which the general idiom is to write to the head and then reverse at the end.

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]).

(Of course, with this specific example, you can alternatively build the results in the reverse order going down to 1 instead of up to M, which would make the lists:reverse call unnecessary.)

If create/2 (or create/3) is not exported and you put an appropriate guard on create/1, the extra N < M guard might be overkill. I generally only check on the exported functions and trust my own internal functions.

平安喜乐 2024-09-05 03:47:13
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]).
create(N,N) -> [N];
create(N,M) -> [N|create(N + 1, M)]. % Don't use ++ to prefix a single element.

This isn't quite the same (you could supply -5), but it behaves the same if you supply meaningful inputs. I wouldn't bother with the extra check anyway, since the process will crash very quickly either way.

BTW, you have a recursion depth problem with the code as-is. This will fix it:

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

create(N, N, Acc) -> [N|Acc];
create(N, M, Acc) -> create(N, M - 1, [M|Acc]).
南笙 2024-09-05 03:47:13

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

第一种是 create/2 第一个子句中的显式相等测试

create(N, M) when N =:= M -> [M];

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

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

在这种情况下,这没有什么区别,因为编译器在内部将模式匹配版本转换为您所拥有的版本书面。您可以安全地选择您认为在每种情况下感觉最好的版本。

在第二种情况下,您需要某种形式的健全性检查,以确保参数的值在您期望的范围内。在每个循环中进行操作是不必要的,我会将其移至 create/1 中的等效测试:

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

如果您想使用累加器,我个人会使用计数版本,因为它可以节省最后反转列表的时间。如果列表不长,我认为差异很小,您可以选择您感觉最清晰的版本。不管怎样,如果你发现它很关键的话,以后更改是很容易的。

I don't really think you have over used guards. There are two cases:

The first is the explicit equality test in the first clause of create/2

create(N, M) when N =:= M -> [M];

Some have suggested transforming this to use pattern matching like

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

In this case it makes no difference as the compiler internally transforms the pattern matching version to what you have written. You can safely pick which version you think feels best in each case.

In the second case you need some form of sanity check that the value of the argument in the range you expect it to be. Doing in every loop is unnecessary and I would move it to an equivalent test in create/1:

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

If you want to use an accumulator I would personally use the count version as it saves reversing the list at the end. If the list is not long I think the difference is very small and you can pick the version which feels most clear to you. Anyway, it is very easy to change later if you find it to be critical.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文