foldr
来表达 filter
:filter p = foldr (\x xs-> if p x then x:xs else xs) []
显然你是为了学习而来,所以让我给你展示一些很酷的东西。首先,为了复习我们的知识,这种过滤器的类型是:
filter :: (a -> Bool) -> [a] -> [a]
[a] -> [a]
。它将一个列表拆分并建立一个新列表。Nothing
的类型是什么?启动 GHCI,它说 Nothing :: Maybe a
,我不会反驳。那么 Just Nothing
呢?再次使用 GHCI,它说 Just Nothing :: Maybe (Maybe a)
,这也是完全有效的,但是嵌入在任意数量的 Just
中的这个值的类型是什么呢,甚至是无限数量的 Just
。即,这个值的类型是什么:
foo = Just foo
Haskell实际上不允许这样的定义,但是稍作调整我们可以定义出这样一个类型:
data Fix a = In { out :: a (Fix a) }
just :: Fix Maybe -> Fix Maybe
just = In . Just
nothing :: Fix Maybe
nothing = In Nothing
foo :: Fix Maybe
foo = just foo
哇,非常接近!使用相同的类型,我们可以创建任意嵌套的nothing
:
bar :: Fix Maybe
bar = just (just (just (just nothing)))
顺便说一下: 有人需要皮亚诺算术吗?
fromInt :: Int -> Fix Maybe
fromInt 0 = nothing
fromInt n = just $ fromInt (n - 1)
toInt :: Fix Maybe -> Int
toInt (In Nothing) = 0
toInt (In (Just x)) = 1 + toInt x
这个 Fix Maybe
类型有点无聊。下面是一个其固定点为列表的类型:
data L a r = Nil | Cons a r
type List a = Fix (L a)
这种数据类型将有助于展示一些递归模式。
有用的事实:在Cons a r
中,r
被称为递归点。
折叠是一种将结构分解的操作。对于列表来说,折叠更为常见。现在可以这样表达折叠的类型:
cata :: (T a -> a) -> Fix T -> a
这可以等价地写成:
cata :: (T a -> a) -> (Fix T -> a)
或者用英语说:
你给我一个将数据类型缩减为值的函数,我会给你一个将它的不动点缩减为值的函数。
实际上,我撒了谎,这个类型真正的定义是:
cata :: Functor T => (T a -> a) -> Fix T -> a
T
仅针对递归站点的类型进行参数化,因此Functor
部分实际上是在说“给我一种操作所有递归站点的方法”。cata f = f . fmap (cata f) . out
Fix t
,通过应用out
(来自Fix
的定义),我们可以使它更容易处理,从而得到一个t (Fix t)
。t (Fix t)
转换为t a
,通过愿景式思考,我们可以使用fmap (cata f)
来实现,我们假设我们能够构造出cata
。t a
,我们想要一个a
,所以我们只需使用f
即可。cata
看起来并不像fold。让我们根据cata
来定义一个fold函数。data L a r = Nil | Cons a r
type List a = Fix (L a)
这需要成为一个函数对象才能发挥作用,这很简单:
instance Functor (L a) where
fmap _ Nil = Nil
fmap f (Cons a r) = Cons a (f r)
因此,专门研究 cata
我们得到:
cata :: (L x a -> a) -> List x -> a
construct :: (a -> b -> b) -> b -> L a b -> b
construct _ x (In Nil) = x
construct f _ (In (Cons e n)) = f e n
fold :: (a -> b -> b) -> b -> List a -> b
fold f m = cata (construct f m)
好的,catamorphisms 逐层打破数据结构。
列表上的 Anamorphisms 是展开。展开不如它们的 fold 对偶那样常见,它们的类型如下:
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
正如您所看到的,变形构建数据结构。这是更通用的类型:
ana :: Functor a => (a -> t a) -> a -> Fix t
这应该立即看起来很熟悉。定义也让人想起了catamorphism。
ana f = In . fmap (ana f) . f
这只是相反的事情。使用ana
构建unfold
比使用cata
构建fold
甚至更简单。请注意Maybe (a, b)
和L a b
之间的结构相似性。
convert :: Maybe (a, b) -> L a b
convert Nothing = Nil
convert (Just (a, b)) = Cons a b
unfold :: (b -> Maybe (a, b)) -> b -> List a
unfold f = ana (convert . f)
filter函数是一个有趣的函数,它可以从catamorphism或anamorphism构建而来。截至目前为止,其他回答这个问题的答案也使用了catamorphisms,但我会两种方式都定义一下:
filter p = foldr (\x xs -> if p x then x:xs else xs) []
filter p =
unfoldr (f p)
where
f _ [] =
Nothing
f p (x:xs) =
if p x then
Just (x, xs)
else
f p xs
是的,我知道在unfold版本中我使用了递归定义,但请原谅我,我教给你很多理论,而且filter本身就不是递归的。
我认为你可以这样使用 map
:
filter' :: (a -> Bool) -> [a] -> [a]
filter' p xs = concat (map (\x -> if (p x) then [x] else []) xs)
filter'(> 1) [1,2,3]
将是:concat [[], [2], [3]] = [2,3]
在prelude中有一个concatMap
可以使代码更简单:Pfilter' :: (a -> Bool) -> [a] -> [a]
filter' p xs = concatMap (\x -> if (p x) then [x] else []) xs
filter'' :: (a -> Bool) -> [a] -> [a]
filter'' p xs = foldr (\x y -> if p x then (x:y) else y) [] xs
我建议您看一下foldr
函数。
filter = (\f -> (>>= (\x -> if (f x) then return x else [])))
关于整数列表
filter2::(Int->Bool)->[Int]->[Int]
filter2 f []=[]
filter2 f (hd:tl) = if f hd then hd:filter2 f tl
else filter2 f tl
我忍不住要用另一种方式回答这个问题,这次完全没有递归。
-- This is a type hack to allow the y combinator to be represented
newtype Mu a = Roll { unroll :: Mu a -> a }
-- This is the y combinator
fix f = (\x -> f ((unroll x) x))(Roll (\x -> f ((unroll x) x)))
filter :: (a -> Bool) -> [a] -> [a]
filter =
fix filter'
where
-- This is essentially a recursive definition of filter
-- except instead of calling itself, it calls f, a function that's passed in
filter' _ _ [] = []
filter' f p (x:xs) =
if p x then
(x:f p xs)
else
f p xs
concat
,你可以更加严格地使用类型,并使用catMaybes :: [Maybe a] -> [a]
。 - dan_waterworth