我正在做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只是一个容器而且是一个适用者。)
我在某个地方犯了什么错误吗?
答案 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
类型,我收集了您所采用的课程,但不允许使用库函数来解决问题。我不太同意这种做法;我认为最好的学习方法是以下两步过程:
因此,我建议您作为子问题,为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
同样,我建议您在编写此代码时充分利用map
和filter
等实用程序函数,并在使其正常工作后,编写自己的函数版本你用作额外的运动。 (实际上,考虑到你的问题中存在非标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