讓pack是一個函式[a] -> [[a]],它接受一個串列并將連續重復的元素分組到子串列中。
下面是packHaskell中的兩個實作。
pack :: (Eq a) => [a] -> [[a]]
pack x = reverse $ foldl f [] x where
f cks@(ck1:_):rest) x
| x == ck1 = (x:ck):rest
| otherwise [x]:cks
f _ x = [[x]]
pack' (x:xs) = let (first,rest) = span (==x) xs
in (x:first) : pack' rest
pack' [] = []
這些實作有一個關鍵的語意差異:如果我們將第一個實作應用于無限串列,例如[1..]. 但是第二種實作確實適用于無限串列。例如,head $ pack' [1..]評估。
我的猜測是該let in符號是惰性的,因此span(在其 Prelude 定義中使用let- in)僅在我們應用pack'無限串列時評估有限多個運算式。
然而,這對我來說是一個不令人滿意的解釋,因為我可以reverse用下面的定義來代替。
reverse' = foldl (\y x0 -> x0:y) []
如果我們這樣做,每個運算式pack從左到右折疊——所以我希望這適用于無限串列——但它仍然掛起。
問題:為什么pack'適用于無限串列而不適用pack?
uj5u.com熱心網友回復:
foldl :: Foldable f => (b -> a -> b) -> b -> f a -> b將對于給定的函式f和z串列的基值產生以下結果:[x1, x2, …, xn]
f (f (… (f (fzx 1 ) x 2 ) …) x n-1 ) x n
如果我們因此想要確定弱頭部范式(WHNF),我們需要訪問串列的最后一個元素。的 fold 函式f的foldl第一個引數可以是惰性的,但我們至少必須使用as 引數進行函式呼叫。這就是為什么檔案上說:xnfoldl
請注意,以生產經營的最外層應用的整個輸入串列必須經過。像所有左關聯折疊一樣,
foldl如果給定一個無限串列,就會發散。
我的猜測是 let in 符號是惰性的,因此當我們在無限串列上應用 pack' 時,span(在其 Prelude 定義中使用 let-in)只會計算有限多個運算式。
你是對的let, ,where子句和所有其他子運算式中的定義都是惰性的。但最終如果您對結果感興趣,則需要確定 WHNF,有時甚至超過 WHNF。
它起作用的原因是因為它span :: (a -> Bool) -> [a] -> ([a], [a])是懶惰地實作的。事實上,span是實作為[SRC] :
span :: (a -> Bool) -> [a] -> ([a],[a]) span _ xs@[] = (xs, xs) span p xs@(x:xs') | p x = let (ys,zs) = span p xs' in (x:ys,zs) | otherwise = ([],xs)
因此,它并沒有需要知道如何在span尾部看起來,以產生一個2元組,其中x已滿足謂詞放在第一項,或在第二個專案,如果p x失敗。
這意味著span將生成一個二元組,其中第一項將包含滿足謂詞的所有元素,而第二項是對要處理的串列其余部分的惰性參考。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/359164.html
上一篇:保護運算式的模式匹配
下一篇:從鏡頭串列創建遍歷
