使用折叠插入

问题描述 投票:3回答:2

有人可以解释我如何使用折叠编写intercalate函数?另外,我听说过foldrfoldl;在这种情况下哪一个最适合使用?

这是我对递归的尝试:

intercalate :: [a] -> [[a]] -> [a]
intercalate s [] = []
intercalate s [x] = x
intercalate s (x:xs) = x ++ s ++ (intercalate s xs)
function haskell fold
2个回答
5
投票

使用foldr1

intercalate' :: [a] -> [[a]] -> [a]
intercalate' _ [] = []
intercalate' x xs = foldr1 (\a acc -> a ++ x ++ acc) xs

这将在每个元素之间插入来自右侧的值和x。 (尽管它确实是这个lazily。 - 直到需要时才评估整个折叠。)

foldr1foldr都是相似的,因为它们从右边折叠。不同之处在于,前者不要求我们提供最终用语,而后者确实需要提供最终用语。这要求我们对空列表进行模式匹配,否则会抛出异常。

实际上,foldl1也可以实现插层。然而,由于左侧折叠将急切地消耗输入,因此强烈建议不要这样做。

intercalate' :: [a] -> [[a]] -> [a]
intercalate' _ [] = []
intercalate' x xs = foldl1 (\acc a -> acc ++ x ++ a) xs

为了进一步强调对正确折叠的偏好,请考虑以下构成:

take 10 . intercalate' [0] $ repeat [1..3]

看起来无害。但是使用foldl1将急切地消耗无限列表,而不是停止进程。

但是,使用foldr1将懒惰地评估并给出[1,2,3,0,1,2,3,0,1,2]的正确结果。 (intercalate'将暂时评估[1..3] ++ [0] ++ (foldr (...) [0] [elem1 ... elemN])之前take 10要求领先条款并评估折叠。)

感谢Will

有用的阅读: foldl versus foldr behaviour with infinite lists


1
投票

让我们提醒自己foldl的签名:

foldl :: (b -> a -> b) -> b -> t a -> b

简单改写你所写的内容:

intercalate :: [a] -> [[a]] -> [a]
intercalate s [] = []
intercalate s [x] = x
intercalate s (x:xs) = foldl (\acc y -> acc ++ s ++ y) x xs
© www.soinside.com 2019 - 2024. All rights reserved.