[英]Difference between concatMap f xs and concat $ map f xs?
據推測,他們做同樣的事情, concatMap f xs
和concat $ map f xs
。 為什么我會選擇一個而不是另一個?
我想這可能是一個優化。 如果是這樣,GHC 7.8仍然如此嗎?
這是你懷疑concatMap f xs = concat (map f xs)
。 因此,為了正確起見,您應該將它們視為可互換的。 不過,我們可以檢查他們的定義以了解更多信息。
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f = foldr ((++) . f) []
concat :: [[a]] -> [a]
concat = foldr (++) []
特別是,這意味着concat . map f
concat . map f
擴展為foldr (++) [] . map f
foldr (++) [] . map f
現在使用被稱為“ fold
通用屬性”的東西,我們可以看到foldr gz . map f = foldr (g . f) z
foldr gz . map f = foldr (g . f) z
代表任何( g
, z
, f
),例如我們上面使用的選擇( (++)
, f
, []
)。 這表明concatMap f = concat . map f
concatMap f = concat . map f
就像我們想要的那樣。[0]
那他們為什么定義不同呢? 因為foldr ((++) . f) []
總是比foldr (++) [] . map f
快foldr (++) [] . map f
foldr (++) [] . map f
因為,在一個真正的病理情況下,后者表明兩個單獨的遞歸。 由於懶惰,不太可能執行兩次遞歸,那么是什么給出了?
真正的原因是,有更復雜的融合的法律提供給編譯器,如那些結合了兩種順序foldr
秒或它定義之間的相互作用foldr
和unfoldr
。 使用它們有點挑剔,因為它們依賴於能夠查看代碼片段的表面語法並檢測可能的簡化。 許多工作都是為了不斷解決融合法。
但我們可以做的一件事是鼓勵人們使用預先應用優化法則的高階組合器。 自foldr (++) [] . map f
foldr (++) [] . map f
永遠不會比foldr ((++) . f) []
更快foldr ((++) . f) []
我們可以采用捷徑並預先應用普遍的法律簡化。 這將提高融合法在其他地方解雇的可能性,以最佳地優化列表生產管道。
[0]為什么這項法律有效? 大致上, foldr
的普遍定律指出,如果你有任何函數q
使得q [] = z
和q (a:as) = fa (q as)
那么q
必須是並且是 foldr fz
。 因為q = foldr gz . map f
q = foldr gz . map f
可以顯示為q [] = z
和q (a:as) = g (fa) (q as)
然后它必須像我們想要的那樣折疊像折疊foldr (g . f) z
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.