在Haskell中使用HoF实现过滤器

时间:2010-09-03 04:10:28

标签: list haskell

我正在尝试编写一个带谓词f和列表的函数,并返回一个列表,该列表包含满足保留顺序的f的所有项。诀窍是只使用更高阶函数(HoF),没有递归,没有理解,当然也没有过滤器。

7 个答案:

答案 0 :(得分:7)

您可以filter

表达foldr
filter p = foldr (\x xs-> if p x then x:xs else xs) []

答案 1 :(得分:6)

我认为您可以这样使用 map

filter' :: (a -> Bool) -> [a] -> [a]
filter' p xs = concat (map (\x -> if (p x) then [x] else []) xs)
你知道吗?转换列表列表中的列表,如果您想要的元素不通过p,则转为空列表

filter' (> 1) [1 , 2, 3 ]将是:concat [ [], [2], [3]] = [2,3]

prelude中, concatMap 可使代码更简单:P

代码应如下所示:

filter' :: (a -> Bool) -> [a] -> [a]
filter' p xs = concatMap (\x -> if (p x) then [x] else []) xs

使用foldr,如sclv所建议,可以使用以下内容完成:

filter'' :: (a -> Bool) -> [a] -> [a]
filter'' p xs = foldr (\x y -> if p x then (x:y) else y) [] xs

答案 2 :(得分:6)

你显然要这样做才能学习,所以让我向你展示一些很酷的东西。首先,为了刷新我们的想法,过滤器的类型是:

filter :: (a -> Bool) -> [a] -> [a]

这个有趣的部分是最后一位[a] -> [a]。它分解了一个列表并构建了一个新列表。

递归模式在Haskell(和其他函数语言)中非常常见,人们已经为这些模式中的某些模式提出了名称。最简单的是变形,它是变形的双重性。我会告诉你最后这与你眼前的问题有什么关系。

固定点

必备知识FTW!

Nothing的类型是什么?解雇GHCI,它说Nothing :: Maybe a我不会反对。那么Just Nothing呢?再次使用GHCI,它说Just Nothing :: Maybe (Maybe a)也是完全有效的,但是这个Nothing嵌入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 s:

bar :: Fix Maybe
bar = just (just (just (just nothing)))

除此之外:Peano算术吗?

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)

这种数据类型将有助于演示一些递归模式。

实用信息:r中的Cons a r称为递归网站

Catamorphism

catamorphism是一种破坏结构的操作。列表的catamorphism更好地称为折叠。现在,变形的类型可以这样表达:

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可以定义为:

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看起来并不像折叠。让我们用cata来定义折叠函数。

重新封装,列表类型为:

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

列表上的Anamorphisms正在展开。与折叠双重相比,展开不太常见,它们的类型如下:

unfoldr :: (b -> Maybe (a, b)) -> b -> [a]

正如您所看到的,anamorphisms构建了数据结构。这是更一般的类型:

ana :: Functor a => (a -> t a) -> a -> Fix t

这应该立即看起来很熟悉。这个定义也让人想起了这种变形。

ana f = In . fmap (ana f) . f

这是相反的事情。从unfold构建ana甚至比从fold构建cata更简单。请注意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)

将理论付诸实践

过滤器是一个有趣的功能,因为它可以从一个变形或变形构造。这个问题的其他答案(迄今为止)也使用了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

是的,是的,我知道我在展开版本中使用了递归定义,但请原谅我,我教你很多理论,无论如何 filter 不是递归的。

答案 3 :(得分:3)

我建议您查看foldr

答案 4 :(得分:2)

嗯,是否允许使用ifs和空列表?

filter = (\f -> (>>= (\x -> if (f x) then return x else [])))

答案 5 :(得分:1)

获取整数列表

filter2::(Int->Bool)->[Int]->[Int]
filter2 f []=[]
filter2 f (hd:tl) = if f hd then hd:filter2 f tl
                else filter2 f tl

答案 6 :(得分:1)

我无法抗拒以另一种方式回答这个问题,这一次没有任何递归。

-- 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