我最近在學習Haskell,遇到了一些我不太理解的東西:lambda函式的引數。
在Learn You a Haskell for Great Good一書中,第5章。5中,有以下兩個函式:
elem' :: (Eq a) => a -> [a] ->Bool
elem' y ys = foldr (x acc -> if x == y then True else acc) False ys
reverse'/span> :: [a] -> [a)
reverse' = foldl (acc x -> x : acc) [] 。
在第一個函式中,累加器被列為lambda的第二個引數,但是在foldl的lambda后面是第一個,我認為這意味著它將是第一個,而不是第二個,因此,違背了預期。
而在第二個函式中,它遵循了預期,顯示為lambda的第一個引數,使得reverse'作為引數的串列成為lambda的第二個引數。
我測驗了這兩個函式,它們都按預期作業。我還注意到,一個函式涉及右折,另一個涉及左折,但我不確定為什么這會改變引數的含義。
問題:誰能解釋一下我錯過了什么?為什么這些引數看起來會互換位置?
誰能解釋一下我錯過了什么?
uj5u.com熱心網友回復:
foldl和foldr期望累積函式的格式不同。這兩個函式有以下型別:
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
foldr :: 可折疊 t => (a -> b -> b) -> b -> t a -> b
你是正確的,在foldr中,累加器是第二個引數,而在foldl中,它是左邊的。
盡管這可能看起來不直觀,但從它們如何關聯串列中的值的角度來考慮
foldl 和 foldr 可能會有所幫助,以下圖片來自 Haskell wiki 上的 "fold" 頁面:

將串列的自然順序視為從左到右:在foldr中,累加器從串列的右側開始,所以它自然是第二個引數,而在 foldl 中,情況正好相反。
uj5u.com熱心網友回復:
這只是一個慣例,在foldr中累加器是第二個引數,而在foldl中它是第一個引數。
為什么要選擇這個慣例呢?
第一個原因已經由 @Joe 回答。
acc是串列的折疊部分。在foldl中它是左邊的部分,但在foldr中它是右邊的部分。所以很自然地提供acc作為左運算元(第一個引數)給foldl中的折疊運算子,作為右運算元(第二個引數)給foldr中的折疊運算子。foldl應該遍歷所提供串列中的所有元素,而foldr不應該。你可以為foldr提供折疊運算子,它可以跳過串列中的其他元素。第一個例子就是這樣做的。在foldr中的第二個引數acc是尚未計算的東西,它持有折疊其余的元素。如果你在你的折疊運算子中跳過它,它就永遠不會被計算了。在你的例子中,如果x == y你只是 "回傳"True(并跳過其余的元素),否則你 "回傳"acc,強制評估串列中的下一個元素。所以,foldr的作業是懶惰的,但foldl的作業是嚴格的。在Haskell中,還有一個慣例。當運算子可以懶惰地作業時,它的第一個引數通常具有嚴格的語意,第二個引數具有非嚴格的語意。比如說。
&&,||就是這種運算子。False & & undefined => False True || undefined => True你的第一個例子中的折疊運算子也是懶惰的。
(x acc -> if x == y then True else acc) y undefined => True而它可以用
||這樣的方式重寫:(x acc -> x == y || acc)
將上述原因結合起來,我們就有了現在的情況:-)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/329813.html
標籤:
上一篇:為GHC.TypeLits.Nat撰寫AbsDiff
下一篇:一種滑動的視窗
