使用折叠插入

时间:2019-03-24 08:13:07

标签: function haskell fold

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

这是我尝试递归的方法:

intercalate :: [a] -> [[a]] -> [a]
intercalate s [] = []
intercalate s [x] = x
intercalate s (x:xs) = x ++ s ++ (intercalate s xs)

2 个答案:

答案 0 :(得分:5)

使用foldr1

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

这将在每个元素之间插入来自{em> right 的值和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 :(得分: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