很难理解折叠...扩展正确吗?也将感谢任何链接或类比会使折叠更易消化。
foldMap :: (a -> b) -> [a] -> [b]
foldMap f [] = []
foldMap f xs = foldr (\x ys -> (f x) : ys) [] xs
b = (\x ys -> (f x):ys)
foldMap (*2) [1,2,3]
= b 1 (b 2 (foldr b [] 3))
= b 1 (b 2 (b 3 ( b [] [])))
= b 1 (b 2 ((*2 3) : []))
= b 1 ((*2 2) : (6 :[]))
= (* 2 1) : (4 : (6 : []))
= 2 : (4 : (6 : []))
答案 0 :(得分:6)
首先,我们不要使用名称foldMap
,因为它已经是a standard function different from map
。如果要使用相同或相似的语义重新实现现有功能,则约定为它赋予相同的名称,但可以在单独的模块中或在其后附加撇号'
名字。另外,我们可以省略空列表的情况,因为您也可以将它传递给首屏:
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (\x ys -> f x : ys) [] xs
现在,如果您想手动评估此功能,请先使用该定义,而无需再插入其他内容:
map' (*2) [1,2,3,4]
≡ let f = (*2)
xs = [1,2,3,4]
in foldr (\x ys -> (f x) : ys) [] xs
≡ foldr (\x ys -> (*2) x : ys) [] [1,2,3,4]
现在只是美化一下:
≡ foldr (\x ys -> x*2 : ys) [] [1,2,3,4]
现在要对此进行评估,还需要定义foldr
。实际上,GHC有点不同,但是有效
foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
以您的示例为例
...
≡ foldr (\x ys -> x*2 : ys) [] (1:[2,3,4])
≡ (\x ys -> x*2 : ys) 1 (foldr (\x ys -> x*2 : ys) [] [2,3,4])
现在我们可以进行β还原:
≡ 1*2 : foldr (\x ys -> x*2 : ys) [] [2,3,4]
≡ 2 : foldr (\x ys -> x*2 : ys) [] [2,3,4]
...然后重复递归。
答案 1 :(得分:5)
foldr
定义了一系列方程,
foldr g n [] = n
foldr g n [x] = g x (foldr g n []) = g x n
foldr g n [x,y] = g x (foldr g n [y]) = g x (g y n)
foldr g n [x,y,z] = g x (foldr g n [y,z]) = g x (g y (g z n))
----- r ---------
,依此类推。 g
是 reducer 函数,
g x r = ....
接受输入列表的元素作为x
,并接受递归处理 r的 r 结果作为r
输入列表中的大多数内容(如公式中所示)。
map
定义了一系列方程式
map f [] = []
map f [x] = [f x] = (:) (f x) [] = ((:) . f) x []
map f [x,y] = [f x, f y] = ((:) . f) x (((:) . f) y [])
map f [x,y,z] = [f x, f y, f z] = ((:) . f) x (((:) . f) y (((:) . f) z []))
= (:) (f x) ( (:) (f y) ( (:) (f z) []))
两个家庭完全匹配
g = ((:) . f) = (\x -> (:) (f x)) = (\x r -> f x : r)
和n = []
,因此
foldr ((:) . f) [] xs == map f xs
我们可以根据foldr
的定义定律,通过对输入列表长度的数学归纳法来严格证明这一点,
foldr g n [] = []
foldr g n (x:xs) = g x (foldr g n xs)
是本文顶部等式的基础。
现代Haskell具有Fodable
类型的类,其基本fold
遵循……的定律
fold(<>,n) [] = n
fold(<>,n) (xs ++ ys) = fold(<>,n) xs <> fold(<>,n) ys
和map
的术语自然定义为
map f xs = foldMap (\x -> [f x]) xs
将[x, y, z, ...]
转换为[f x] ++ [f y] ++ [f z] ++ ...
,因为对于列表(<>) == (++)
。这是由于等价
f x : ys == [f x] ++ ys
这也使我们可以轻松地沿相同的行定义filter
,就像
filter p xs = foldMap (\x -> [x | p x]) xs
对于您的特定问题,扩展名是正确的,除了(*2 x)
应该写为((*2) x)
,它与(x * 2)
相同。 (* 2 x)
不是有效的Haskell(尽管有效的Lisp :))。
诸如(*2)
之类的功能称为“ operator sections”-缺少的参数进入空白位置(* 2) 3 = (3 * 2) = (3 *) 2 = (*) 3 2
。