2010-04-17 54 views
3

我想让我的脑袋围绕一些基本的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使这个尾部递归吗?

+0

如果你想知道是否是尾递归,是否有什么需要返回函数返回前的调用值。在这种情况下,cons正在等待结果,所以不,它不是尾递归。 – Dustin 2010-04-17 22:14:31

+0

这是我在这里回答你的问题的一个的改写:http://stackoverflow.com/questions/2658631/tail-recursion-in-erlang/2658766#2658766 – 2010-04-18 06:15:03

+0

@Jeremy这是不是一个完整的改写,因为我期待有关if语句指导,但我拿点,不会再发布关于这一主题。 – dagda1 2010-04-18 10:03:57

回答

5

不,您使用Acc不会使其尾递归。如果您的分支返回[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)]. 
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条目模式匹配有助于确保(取决于这是什么,你可能会或可能不希望这)
+1

有趣的观点,有没有在“数据库”,使尾递归版本的重要顺序没有'名单:reverse'应该是最快的解决方案。 – 2010-04-19 19:18:07