要回答这个问题,最好回忆一下做什么foldr
和map
做什么。
两者中比较复杂的是foldr
, 有类型
-- list to be folded
-- v
foldr :: (a -> b -> b) -> b -> [a] -> b
-- ^ ^
--folding function terminal value
要折叠的列表实际上是一个 conses 链(:)
和一个终端空列表:
1 : 2 : 3 : []
的作用foldr
是将:
和[]
构造函数分别替换为折叠函数和终止值:
foldr (+) 0 (1 : 2 : 3 : []) == 1 + 2 + 3 + 0
map
功能更简单。一种思考方式是获取一个函数和一个列表,并将该函数应用于列表的每个参数:
map :: (a -> b) -> [a] -> [b]
-- ^ ^
-- function list
但是,您也可以将其视为一个函数,并将其提升为一个作用于列表的函数:
map :: (a -> b) -> ( [a] -> [b] )
-- ^ ^
-- function function on lists
组合这两个函数是什么意思map . foldr
?请注意,这只是一个接一个地应用功能 - 特别是,
(map . foldr) f == map (foldr f)
由于您foldr
首先应用,因此您必须将其应用到一个函数f :: a -> b -> b
,然后您将返回另一个函数:
foldr f :: b -> [a] -> b
-- ^ ^
--terminal val list to be folded
现在你 apply map
,它提升了作用于列表的功能:
map (foldr f) :: [b] -> [[a] -> b]
-- ^ ^
--list of terminal vals functions that fold lists
这种类型看起来很奇怪,但它是有效的。现在不是单个终端值,而是给它一个终端值列表,然后返回一个折叠函数列表 - 一个用于您提供的每个终端值。
为了更清楚,我们可以查看一个特定的函数(+)
,它具有类型
(+) :: Num a => a -> a -> a
如果我们把它代入上面的等式,我们得到
(map . foldr) (+) :: Num a => [a] -> [[a] -> a]
-- ^ ^
-- list of terminal vals functions that fold lists
如果我们现在将它应用到列表中[0, 1, 2]
,我们会得到一个包含三个函数的列表:
(map . foldr) (+) [0,1,2] :: Num a => [[a] -> a]
我们可以使用map ($x)
成语将列表中的每个函数应用于特定参数。它必须是一个数字列表,我会选择[3,4,5]
. 仔细观察:
> map ($[3,4,5]) ((map.foldr) (+) [0,1,2])
[12, 13, 14]
该列表[3,4,5]
被用作折叠函数折叠了三次(+)
,每次都使用不同的终端值:
3 + 4 + 5 + 0 == 12
3 + 4 + 5 + 1 == 13
3 + 4 + 5 + 2 == 14
当终端值为 时0
,我们只需得到值的总和:3 + 4 + 5 == 12
。当终值是1
我们得到的值比总和多 113
时,当终值是我们得到的值2
比总和多 214