2010-04-17 55 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