干得好,你自己发现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
现在我们可以在 Hoogle 中搜索 typet1 -> (t -> t1 -> t1) -> [t] -> t1
。最上面的结果是foldr
,它的类型是(a -> b -> b) -> b -> [a] -> b
- 相同,foo
但变量重命名并且参数顺序颠倒了。搜索结果还告诉我们该函数在Prelude
模块中——Haskell 默认加载的模块。您可以单击结果以在文档中找到其定义,该文档使用以下等式对其进行了描述:
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