【问题标题】:Erlang code critiqueErlang 代码评论
【发布时间】:2011-02-09 05:39:34
【问题描述】:

我正在尝试了解一些基本的 erlang 功能,我可以在下面使用一些 cmets。

我有以下 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 使这个尾递归吗?

【问题讨论】:

  • 如果你想知道某个东西是否是尾递归的,想想是否有什么东西需要调用的返回值才能让函数返回。在这种情况下,cons 正在等待结果,所以不,它不是尾递归。
  • 这是对我在这里为您回答的问题的改写:stackoverflow.com/questions/2658631/tail-recursion-in-erlang/…
  • @Jeremy 这不是一个完整的改写,因为我正在寻找有关 if 语句的指导,但我明白这一点,不会再发布关于该主题的帖子。

标签: erlang


【解决方案1】:

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

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

-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 模式匹配有助于确保这一点(取决于它的用途,你可能想要也可能不想要这个)

【讨论】:

  • 有趣的一点是“数据库”中没有重要的顺序,所以没有lists:reverse的尾递归版本应该是最快的解决方案。
【解决方案2】:

不,您对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]).

但我认为这是可以应用 Myth: Tail-recursive functions are MUCH faster than recursive functions 的示例,所以我会使用更简单的递归版本:

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

【讨论】:

    猜你喜欢
    • 2011-07-24
    • 2011-06-09
    • 2012-09-30
    • 2018-12-10
    • 1970-01-01
    • 2011-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多