2011-01-21 58 views
1
sum :: (Num a) => [a] -> a 
sum xs = foldl (\acc x -> acc + x) 0 xs 

foldl從左側摺疊列表。所以首先我們得到acc=0並把列表xs放到x,然後執行函數->acc+x。經過計算,我們得到新的acc,它等於acc+x。但爲什麼呢?我認爲這個結果acc+x是基於函數x->acc+x的x的新值。解釋如何使用foldl的這個特定功能

+0

這是什麼問題? – delnan 2011-01-21 16:51:10

回答

3

讓我們來看看你的定義綜上所述

sum :: (Num a) => [a] -> a 
sum xs = foldl (\acc x -> acc + x) 0 xs 

我們還採取偷看在與foldl的簽名:

foldl :: (a -> b -> a) -> a -> [b] -> a 

嗯,好吧,我們必須餵食foldl以獲得最終價值(->a)?

  1. 它需要一個咖喱功能(a->b->a)。雖然所有這些都不準確,但是爲了簡潔起見,我們會說它有一個函數需要兩個參數(但是我們知道實際上只需要一個參數並返回帶有一個參數的另一個函數)。

  2. 它需要一個值類型a。請注意,步驟1中的curried函數採用某種類型的a,並返回a類型的東西。有趣...嗯...

  3. 它需要一個類型列表b。請注意,步驟1中的咖喱功能以及a類型的東西,b類型的東西。

那麼,我們要給它它想要的嗎?

  1. 我們給它(\acc x -> acc + x)。這是一個匿名功能,或lambda,它需要兩個參數,(記住,它是咖喱,雖然),accx,並返回它們的總和。

  2. 我們給它0爲出發值

  3. 我們給它xs作爲列表摺疊。

好的dokie。所以,讓我們讓foldl工作它的Haskell魔法。讓我們想象一下,我們所謂的sum [1,2,3]

foldl調用我們的功能(\acc x -> acc + x),使用0accxs的第一個值,1

0 + 1 

這個結果確實獲取存儲在遠離或accx,因爲他們只是在我們的小lambda函數的參數。 foldl將使用該值(請參閱SanSS的具體實現答案)。

請記住,我們的lambda函數的結果與第一個參數的類型相同嗎? foldl可以使用該先前的總和,並將其傳回給lambda函數,以及第二個元素第二個元素。

(0 + 1) + 2 

又一次,直到它已經這樣做了所有元素:

((0 + 1) + 2) + 3 
6 

正如Dan指出的那樣,這是相同的,如果你做了:

sum xs = foldl (+) 0 xs 

你可以用這個函數可以更容易地告訴我們,我們不只是'設置'一些變量並添加到它上面。

希望這會有所幫助。


旁註: 爲了您和的定義,你不必明確規定sum需要xs。你可以把它作爲:

sum = foldl (\acc x -> acc + x) 0

這需要鑽營的優勢,因爲如果我們提供了與foldl只是它的前兩個參數 - 一個咖喱之類的函數(a->b->a)a類型的值 - 我們怎麼得到?

[b] -> a

這需要b類型的列表,並返回a類型的值的函數!這叫做pointfree style。只是要考慮:-)

+0

我對這個lambda函數感到困惑。現在,我明白了。謝謝! – CathyLu 2011-01-23 21:18:59

3

你應該看看與foldl的定義:

foldl f z []  = z 
foldl f z (x:xs) = foldl f (f z x) xs 

與foldl臨危一個函數,它需要兩個參數,一個值(「啓動值」或蓄電池)和一個列表。 如果列表爲空,則返回當前計算。 如果情況不是空的,則它使用與函數相同的函數遞歸調用,累加器是使用累加器作爲第一個參數並將列表的第一個元素作爲第二個參數和尾部的函數調用的結果列表被用作遞歸調用的列表。

因此,sum中使用的lambda函數變得相當清楚,它將acc作爲第一個參數,將list的元素作爲第二個參數,並返回兩者的總和。

的調用的結果:

sum [1,2,3] = ((0 + 1) + 2) + 3 = 6 
1

從你的問題,它聽起來就像你不知道如何lambda函數(\acc x -> acc + x)在這裏工作。

該功能不是x->acc+x,而是acc x->acc + x。事實上,你可以重寫「和」等式

sum xs = foldl (+) 0 xs 

由於(\acc x -> acc + x)相同(+)

我建議你(重新)閱讀http://learnyouahaskell.com/higher-order-functions#lambdas