过滤申请人

时间:2014-07-24 15:56:46

标签: haskell

我正在做NICTA Haskell课程并坚持Applicative的最后一部分,这是我的进步。

基本上,我们要使用在Applicative上下文中生成List的谓词来过滤List。

filtering :: Applicative f => (a -> f Bool) -> List a -> f (List a)
filtering _ Nil = pure Nil
filtering f (x :. rest) = 
        let whole = lift2 (:.) (pure x) (filtering f rest) in
        lift2 filter (const <$> f x) whole

所以whole这里是完整的未过滤列表,因此我提升filter(const <$> f x)中添加的const是为了满足{{1}中的(a -> Bool)要求}}

一切都很好并且编译成功,但是它失败了一个测试用例,所以这里肯定有问题。

例如,filter只返回filtering (Id . even) [4,5,6]而不是Id [4](Id只是一个容器而且是一个适用者。)

我在某个地方犯了什么错误吗?

4 个答案:

答案 0 :(得分:6)

这里的问题是(const <$> f x)。该函数将用于检查整个列表,但它只会在当前f上提供x的结果。这意味着当它在[5,6]子列表上工作时,它基本上是filter (const False) [5,6],这会产生一个空列表。

您无法呼叫filter为您执行此操作,因为它的形状不正确。这样的事总会发生。相反,只需专注于包含当前元素,并让递归正确处理列表的其余部分。 (我不想多说,因为做这门课程的目的当然是为自己解决这个问题。)

答案 1 :(得分:4)

我对你认为需要“完整的未经过滤的清单”感到困惑,我想你也是。完整的未经过滤的列表x :. rest,你已经有了!

显然,你的意思是“整个结果,如果谓词总是产生True ......但这没有任何意义,因为你基本上预测它会< / em>总是产生True

你需要考虑的是,而不是这个whole的东西,是两个应用程序包装的值:头部谓词的结果 - 只是f x :: f Bool。并且,作为递归,列表的已过滤的其余部分filtering f rest :: f (List a)

现在,正如您明显注意到liftA2通常是组合两个Applicative - 包装值的最简单方法(尽管<*>实际上倾向于制作更整洁的代码)。召回

liftA2 :: (a->b->c) -> f a -> f b -> f c

在这种情况下,

liftA2 :: (Bool->List a->List a) -> f Bool -> f (List a) -> f (List a)

所以你需要一个函数Bool->List a->List a,如果布尔值为真,它会在x之前加上,否则就会保留列表。好吧,在本地where块中定义它应该不是问题。

答案 2 :(得分:1)

这个问题要简单得多,如果不是在一个函数中尝试它,而是将它分解为更小的子问题。

我给出的第一个提示是它看起来非常像Traversable类的一个问题,其中包括这个方法,它的签名应该让你去哼哼!&#34;嗯!&#34 ;:

traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)

通过问题中有趣的List类型,我收集了您所采用的课程,但不允许使用库函数来解决问题。我不太同意这种做法;我认为最好的学习方法是以下两步过程:

  1. 使用库函数解决问题。通过这种方式,您可以学习库,更重要的是,您将学习如何将问题分解为更小,更通用的库。
  2. 编写您自己的库函数版本。通过这种方式,您可以了解基于库的解决方案的工作原理。
  3. 因此,我建议您作为子问题,为Traverse类型编写自己的List版本。

    下一个子问题:使用traverse,编写此函数应该很简单:

    tagWithBool :: (a -> f Bool) -> -> List a -> f (List (Bool, a))
    

    一旦你有了这个,我要做的下一步就是编写这个函数:

    -- Remove the items tagged with `False`, and eliminate the tags.
    removeFalse :: List (Bool, a) -> List a
    

    同样,我建议您在编写此代码时充分利用mapfilter等实用程序函数,并在使其正常工作后,编写自己的函数版本你用作额外的运动。 (实际上,考虑到你的问题中存在非标List类型,你几乎不得不编写自己的版本来解决这个问题。)

    完成所有这些后,您可以写下:

    filtering :: Applicative f => (a -> f Bool) -> List a -> f (List a)
    filtering p xs = fmap removeFalse (tagWithBool xs)
    

    请注意,这会使用fmap,因此您必须为Functor类型(或仅List函数)实施mapList :: (a -> b) -> List a -> List b

答案 3 :(得分:0)

这个怎么样(我正在使用普通列表,而不是你的ctor)

seq f [] = []
seq x:xs = lift2 (:) x (seq xs)
remove (x:xs) (y:ys) = (if y then [x] else []) ++ remove xs ys

filterapp f xs = lift2 remove (pure xs) $ seq $ map f xs