有人可以向我解释如何使用fold编写intercalate
函数吗?另外,我听说过foldr
或foldl
;在这种情况下,哪一种最合适?
这是我尝试递归的方法:
intercalate :: [a] -> [[a]] -> [a]
intercalate s [] = []
intercalate s [x] = x
intercalate s (x:xs) = x ++ s ++ (intercalate s xs)
答案 0 :(得分:5)
使用foldr1
:
intercalate' :: [a] -> [[a]] -> [a]
intercalate' _ [] = []
intercalate' x xs = foldr1 (\a acc -> a ++ x ++ acc) xs
这将在每个元素之间插入来自{em> right 的值和x
。 (尽管这样做lazily。–直到需要时才会评估整个折痕。)
foldr1
和foldr
的相似之处在于它们从右侧折叠。不同之处在于,前者不需要我们提供最终期限,而后者确实需要提供最终期限。这要求我们对空列表进行模式匹配,否则将引发异常。
实际上,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表示敬意。
答案 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