好的,我们可以这样评论Churchlist类型来澄清它:
-- Tell me...
type Churchlist t u = (t -> u -> u) -- ...how to handle a pair
-> u -- ...and how to handle an empty list
-> u -- ...and then I'll transform a list into
-- the type you want
请注意,这与
foldr
函数密切相关:
foldr :: (t -> u -> u) -> u -> [t] -> u
foldr k z [] = z
foldr k z (x:xs) = k x (foldr k z xs)
foldr
是一个非常通用的函数,可以实现各种其他列表函数。一个简单的例子是使用foldr
实现列表复制:
copyList :: [t] -> [t]
copyList xs = foldr (:) [] xs
使用上面注释的类型,
foldr (:) []
的意思是:“如果你看到一个空列表,返回一个空列表;如果你看到一对,返回
head:tailResult
。”
使用
Churchlist
,你可以轻松地这样编写对应代码:
-- Note that the definitions of nil and cons mirror the two foldr equations!
nil :: Churchlist t u
nil = \k z -> z
cons :: t -> Churchlist t u -> Churchlist t u
cons x xs = \k z -> k x (xs k z)
copyChurchlist :: ChurchList t u -> Churchlist t u
copyChurchlist xs = xs cons nil
现在,要实现map
,您只需要用适当的函数替换cons
,就像这样:
map :: (a -> b) -> [a] -> [b]
map f xs = foldr (\x xs' -> f x:xs') [] xs
映射类似于复制列表,但不仅是逐字复制元素,而是将f
应用于每个元素。
仔细研究这些内容,您应该能够自己编写mapChurchlist ::(t -> t') -> Churchlist t u -> Churchlist t' u
。
额外练习(简单):使用foldr
编写这些列表函数,并编写相应的Churchlist
函数:
filter :: (a -> Bool) -> [a] -> [a]
append :: [a] -> [a] -> [a]
find :: (a -> Bool) -> [a] -> Maybe a
如果你感觉可以尝试一些更难的事情,可以尝试为Churchlist
编写tail
函数。(先使用foldr
为[a]
编写tail
函数)