Erlang代码批判

时间:2010-04-17 20:25:37

标签: erlang

我试图了解一些基本的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使这个尾递归?

2 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

如上所述,有一个标准模块功能已经这样做(proplists:delete)。不应该多说,但是......

我倾向于保留原始方法名称(删除),但是有一个本地版本,包括累加器作为参数。上下文让我觉得“数据库”中元组的顺序无关紧要,所以列表: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]).

这里有几件事:

  • 尾递归;总的来说,我认为坚持尾递归是更安全的 - 虽然对身体递归调用进行了优化,但你真的需要用真实的(对你的应用程序)数据进行一些性能测量来进行比较
  • 请注意,我们不接受任何旧列表;删除/ 3中的条目模式匹配有助于确保这一点(取决于它的用途,您可能想要或不想要这个)