2012-03-30 82 views
6

根據Learn you some Erlang褶皺與遞歸二郎

差不多,你能想到的任何功能減少列表1元可表示爲一個摺疊。 [...] 這意味着折是在這個意義上通用的,你可以用摺疊落實名單幾乎任何其他遞歸函數

我首先想到寫一個函數,它接受一個列表,並減少了當1元素是使用遞歸。

什麼是應該幫助我決定是否使用遞歸或摺疊的指導方針?

這是一種文體考慮還是還有其他因素(性能,可讀性等)?

回答

8

褶皺通常都更具有可讀性(因爲每個人都知道他們在做什麼)和更快由於在運行時優化的實現(特別是與foldl總是應該是尾遞歸)。值得注意的是,它們只是一個恆定的因素,而不是另一個訂單,所以如果您因性能原因而發現自己考慮了另一個,通常是不成熟的優化。

當你喜歡做某些事情時,使用標準遞歸,例如一次處理多個元素,分裂成多個進程和類似的東西,並且當他們使用高階函數時(摺疊,映射......)已經做到了你想要的。

3

我期望fold是遞歸完成的,所以你可能想看看試圖用fold實現一些各種列表函數,比如map或者filter,看看它是多麼有用。

否則,如果您正在遞歸執行此操作,則基本上可能會重新實現fold操作。

學會使用語言,是我的想法。

上與foldl和遞歸這個討論很有意思:

Easy way to break foldl

如果你看一下在此介紹的第一款(您可能需要閱讀所有的話),他說比我更好。

http://www.cs.nott.ac.uk/~gmh/fold.pdf

9

我個人比較傾向於Erlang中的遞歸(與其他語言,例如Haskell相反)。我看不到摺疊比遞歸更可讀。例如:

fsum(L) -> lists:foldl(fun(X,S) -> S+X end, 0, L). 

fsum(L) -> 
    F = fun(X,S) -> S+X end, 
    lists:foldl(F, 0, L). 

VS

rsum(L) -> rsum(L, 0). 

rsum([], S) -> S; 
rsum([H|T], S) -> rsum(T, H+S). 

似乎更代碼,但它是非常簡單和慣用的Erlang。使用摺疊需要更少的代碼,但隨着更多有效負載的差異變得越來越小。想象一下,我們想要一個過濾器並將奇數值映射到它們的正方形。

lcfoo(L) -> [ X*X || X<-L, X band 1 =:= 1]. 

fmfoo(L) -> 
    lists:map(fun(X) -> X*X end, 
    lists:filter(fun(X) when X band 1 =:= 1 -> true; (_) -> false end, L)). 

ffoo(L) -> lists:foldr(
    fun(X, A) when X band 1 =:= 1 -> [X|A]; 
     (_, A) -> A end, 
    [], L). 

rfoo([]) -> []; 
rfoo([H|T]) when H band 1 =:= 1 -> [H*H | rfoo(T)]; 
rfoo([_|T]) -> rfoo(T). 

在這裏列表理解勝,但遞歸函數是第二位,摺疊版本是醜陋的和可讀性較差。

最後,fold比遞歸版本更快,尤其是編譯爲本機(HiPE)代碼時。

編輯: 我添加了一個摺疊的版本與可變樂趣的要求:

ffoo2(L) -> 
    F = fun(X, A) when X band 1 =:= 1 -> [X|A]; 
      (_, A) -> A 
     end, 
    lists:foldr(F, [], L). 

我不明白它是如何比rfoo/1更具可讀性,我發現特別是蓄電池的操作更加複雜,比直接遞歸更不明顯。它甚至更長的代碼。

+5

不要忘記'列表:zf/2'。這是一個地圖過濾器。有了zf,你可以做'list:zf(fun(X)當X band 1 =:= 1 - > {true,X * X};(_) - > false end,L)'。不幸的是zf沒有記錄,所以不正式存在... – 2012-03-30 15:55:13

+0

不同的方法比較好!我認爲你的第一個例子中摺疊更具可讀性,但是這樣一個簡單函數的差異當然很小。在第二個例子中,我發現fmfoo比rfoo更具可讀性,但是當然,列表理解爲這個問題贏得了很大的時間。所有這些都表明,開發人員每次都要爲這項工作選擇合適的工具真的取決於:-) – 2012-03-31 10:15:37

+0

很好的答案btw +1。 – Gilles 2012-04-02 12:53:51