干得好,您自己发现了foldr! (我希望这听起来没有嘲讽之类的意思,不是那个意思;大多数人都觉得褶皱不自然,并且必须非常努力地思考才能理解它!)
我建议你处理这些情况的方法是尝试自己编写你想要的函数,并弄清楚类型,然后在Hoogle中搜索该类型,看看是否已经存在这样的函数。
在这种情况下,您可以尝试以这种方式编写函数。我们称之为foo:
-- If we see an empty list the result should be u
foo u f [] = u
-- If we're given a a non-empty list we recurse down the list to get a partial
-- result, then "add on" to it:
foo u f (x:xs) = f x (foo u f xs)
一旦你定义了这个函数,你就可以将它加载到ghci 并使用它的:t 命令来查找它的类型:
*Main> :load "../src/scratch.hs"
[1 of 1] Compiling Main ( ../src/scratch.hs, interpreted )
Ok, modules loaded: Main.
*Main> :t foo
foo :: t1 -> (t -> t1 -> t1) -> [t] -> t1
现在我们可以search Hoogle for the type t1 -> (t -> t1 -> t1) -> [t] -> t1。最上面的结果是foldr,其类型为(a -> b -> b) -> b -> [a] -> b——与foo 相同,但变量重命名并且参数顺序颠倒了。搜索结果还告诉我们该函数在 Prelude 模块中——Haskell 默认加载的模块。你可以点击结果找到its definition in the documentation,它用这个等式描述:
foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)
他们使用f 函数作为中缀运算符,我希望这不会让您感到困惑,但以防万一我们可以将其重写为:
foldr f z [x1, x2, ..., xn] == f x1 (f x2 ... (f xn z) ...)
这正是你想要的行为。
为什么我对上面的foldr 做了这么大的事情?因为foldr 实际上是拆分列表的最基本功能。这样看类型:
foldr :: (a -> b -> b) -- ^ What to do with a list node
-> b -- ^ What to do with the empty list
-> [a] -- ^ A list
-> b -- ^ The final result of "taking the list apart."
原来很多列表函数都可以用foldr来写:
map f = foldr step []
where step x rest = (f x):rest
-- | Append two lists
xs (++) ys = foldr (:) ys xs
-- | Filter a list, keeping only elements that satisfy the predicate.
filter pred = foldr step []
where step x rest | pred x = x:rest
| otherwise = rest
-- | Find the first element of a list that satisfies the predicate.
find pred = foldr step Nothing
where step x r | pred x = Just x
| otherwise = r