Erlang 代码评论

发布于 2024-08-29 10:16:46 字数 482 浏览 4 评论 0原文

我正在尝试了解一些基本的 erlang 功能,并且我可以对以下内容进行一些评论。

我有以下 erlang 代码,它接受一个元组列表,如果找到一个键,则返回一个减去元素的列表:

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    Acc;
remove([H|T], Key, Acc) ->
    if
        element(1, H) /= Key ->             
            [H| remove(T, Key, Acc)];
        true  -> 
            remove(T, Key, Acc)
    end.

这是这样做的好方法吗?

if 语句似乎不正确。

我对累加器 Acc 的使用是否使这个尾部递归?

I am trying to get my head round some basic erlang functionality and I could do with some comments on the following.

I have the following erlang code that takes a list of tuples and returns a list minus an element if a key is found:

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    Acc;
remove([H|T], Key, Acc) ->
    if
        element(1, H) /= Key ->             
            [H| remove(T, Key, Acc)];
        true  -> 
            remove(T, Key, Acc)
    end.

Is this a good way of doing this?

The if statement seems incorrect.

Also is my use of the accumulator Acc making this tail recursive?

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

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

发布评论

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

评论(2

帅的被狗咬 2024-09-05 10:16:46

不,您对 Acc 的使用不会使其成为尾递归。你的 if 分支返回 [H| remove(T, Key, Acc)] 这不是尾调用,并且大多数时间都会使用此分支。更准确地说,您对 Acc 的使用是无用的,因为它始终是 [] ,您根本不会更改它的值。正确的代码应该是这样的。

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    lists:reverse(Acc);
remove([H|T], Key, Acc) ->
    if
        element(1, H) /= Key ->             
            remove(T, Key, [H|Acc]);
        true  -> 
            remove(T, Key, Acc)
    end.

但如果你的列表成员总是成对的,我更喜欢直接模式匹配:

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    lists:reverse(Acc);
remove([{Key, _}|T], Key, Acc) ->
    remove(T, Key, Acc);
% if it should delete only first occurrence then lists:reverse(Acc, T);
remove([H|T], Key, Acc) ->
    remove(T, Key, [H|Acc]).

但我认为这是可以应用 神话:尾递归函数比递归函数快得多,所以我会使用更简单的递归版本:

delete(Key, []) -> [];
delete(Key, [{Key, _}|T]) -> delete(Key, T);
% if it should delete only first occurrence then just T;
delete(Key, [H|T]) -> [H | delete(Key, T)].

No, your usage of Acc doesn't make it tail recursive. Your branch of if returns [H| remove(T, Key, Acc)] which is not tail call and this branch would be used most of time. To be more precise your usage of Acc is useless because it would be [] whole time, you don't change its value at all. Correct code should look like.

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    lists:reverse(Acc);
remove([H|T], Key, Acc) ->
    if
        element(1, H) /= Key ->             
            remove(T, Key, [H|Acc]);
        true  -> 
            remove(T, Key, Acc)
    end.

But if your list members are always pairs I would prefer direct pattern match:

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    lists:reverse(Acc);
remove([{Key, _}|T], Key, Acc) ->
    remove(T, Key, Acc);
% if it should delete only first occurrence then lists:reverse(Acc, T);
remove([H|T], Key, Acc) ->
    remove(T, Key, [H|Acc]).

But I think this is example where can apply Myth: Tail-recursive functions are MUCH faster than recursive functions so I would use much simpler recursive version:

delete(Key, []) -> [];
delete(Key, [{Key, _}|T]) -> delete(Key, T);
% if it should delete only first occurrence then just T;
delete(Key, [H|T]) -> [H | delete(Key, T)].
网白 2024-09-05 10:16:46

如前所述,有一个标准模块函数已经可以执行此操作(proplists:delete)。不需要多说,但是......

我倾向于保留原始方法名称(删除),但有一个本地版本,包括累加器作为参数。上下文让我认为“数据库”中元组的顺序并不重要,因此列表:反向是不必要的。

-module(foo).
-export([delete/2]).

delete(Key, Database) ->
    delete(Key, Database, []).

delete(_Key, [], Acc) ->
    Acc;
delete(Key, [{Key, _} | T], Acc) ->
    delete(Key, T, Acc);
delete(Key, [Entry={_, _} | T], Acc) ->
    delete(Key, T, [Entry | Acc]).

这里有一些事情:

  • 尾递归;一般来说,我认为坚持使用尾递归更安全——虽然对主体递归调用进行了优化,但您确实需要使用实际的(对于您的应用程序)数据进行一些性能测量,以便进行
  • 比较我们不接受此处的任何旧列表; delete/3 中的 Entry 模式匹配有助于确保这一点(取决于它的用途,您可能需要也可能不需要)

As mentioned, there is a standard module function that already does this (proplists:delete). Shouldn't need to say more, but...

I'd tend to keep the original method name (delete), but have a local version including the accumulator as parameter. The context makes me think the order of the tuples in the "database" doesn't matter, so that a lists:reverse isn't necessary.

-module(foo).
-export([delete/2]).

delete(Key, Database) ->
    delete(Key, Database, []).

delete(_Key, [], Acc) ->
    Acc;
delete(Key, [{Key, _} | T], Acc) ->
    delete(Key, T, Acc);
delete(Key, [Entry={_, _} | T], Acc) ->
    delete(Key, T, [Entry | Acc]).

A couple things here:

  • tail-recursive; in general I think it is safer to stick with tail-recursion -- while there are optimizations for body recursive calls, you'd really need to do some performance measurement with realistic (for your application) data to make a comparison
  • note that we're not accepting any old list here; the Entry pattern match in delete/3 helps ensure this (depending on what this is for, you may or may not want this)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文