-- This works: foldr go1 [] [1..]
-- This doesn't: foldr go2 [] [1..]
go1 a b = a : b
go2 a [] = a : []
go2 a b = a : b
與go1
摺疊馬上開始返回值,但go2
似乎等待列表的末尾。
很明顯,模式匹配導致某些東西被不同地處理。有人可以解釋這裏究竟發生了什麼嗎?
-- This works: foldr go1 [] [1..]
-- This doesn't: foldr go2 [] [1..]
go1 a b = a : b
go2 a [] = a : []
go2 a b = a : b
與go1
摺疊馬上開始返回值,但go2
似乎等待列表的末尾。
很明顯,模式匹配導致某些東西被不同地處理。有人可以解釋這裏究竟發生了什麼嗎?
與go1
不同,go2
檢查其第二個參數是否爲空。爲了做到這一點,需要對第二個參數進行評估,至少要確定它是否爲空。
因此,對於您的來電foldr
這意味着:
兩個go1
和go2
首先調用兩個參數:1和foldr go [] [2 ..]
結果。在go1
的情況下,第二個參數保持不變,因此foldr
的結果只是1 :: foldr go [] [2 ..]
,而不進一步評估尾部,直到它被訪問。
然而在go2
的情況下,需要評估foldr go [] [2 ..]
以檢查它是否爲空。要做到這一點foldr go [] [3 ..]
然後需要評估出於同樣的原因。等無止境。
這是因爲懶惰....由於本示例中定義了go1
和go2
的方式,它們的行爲與b==[]
的行爲完全相同,但編譯器不知道這一點。
對於go1
,最左邊的摺疊將使用尾遞歸來立即輸出a的值,然後計算b的值。
go1 a b -> create and return the value of a, then calculate b
對於go2
,編譯器甚至不知道要匹配的情況下,直至b
值計算....這永遠不會發生。
go2 a b -> wait for the value of b, pattern match against it, then output a:b
要測試一個表達式是否滿足某種模式,至少需要將其評估爲弱頭法線形式。因此模式匹配力評估。 一個常見的例子是交織兩個列表的interleave
函數。它可以像
interleave :: [a] -> [a] -> [a]
interleave xs [] = xs
interleave [] ys = ys
interleave (x:xs) (y:ys) = x : y : interleave xs ys
但是這個函數在第二個參數中是嚴格的。而更懶的版本是
interleave [] ys = ys
interleave (x:xs) ys = x : interleave ys xs
如果它「交錯兩個名單」,不應該被稱爲「交錯」? 「合併」更適合於例如mergesort ... – 2014-11-04 16:00:44
@Will Ness,固定。我已經看到這個函數多次被稱爲「合併」。 – user3237465 2014-11-04 17:09:00
要看到差距試試這個在GHCI:
> head (go1 1 (error "urk!"))
1
> head (go2 1 (error "urk!"))
*** Exception: urk!
的問題是,go2
將之前評估其第二個參數返回列表的頭部。即go2
是嚴格在其第二個參數,不像go1
這是懶惰。
當你摺疊了無限的名單這一點很重要:
fold1 go1 [] [1..] =
go1 1 (go1 2 (go1 3 (..... =
1 : (go1 2 (go1 3 (..... =
1 : 2 : (go1 3 (...
工作正常,但
fold1 go1 [] [1..] =
go2 1 (go2 2 (go2 3 (.....
不能因爲go2
評估其第二個參數,這是另一個呼叫堅持簡化爲1:...
到go2
,這又需要自己的第二個參數進行評估,這是另一個...
好吧,你明白了。第二個不會停下來。
感謝大家的出色答案。如果我可以選擇多種解決方案,我會的,因爲他們都幫助我理解了這個概念。 – Balthamos 2014-11-02 23:48:59
除了「選擇」答案之外,您還可以提升那些您認爲「有用」的答案(當您將鼠標懸停在向上箭頭上時這樣說)。 :) – 2014-11-04 15:50:07