Haskell 过滤应用程序
我正在学习NICTA Haskell课程,坚持学习应用程序的最后一部分,以下是我的学习进度 基本上,我们要用谓词过滤列表,该谓词在应用程序上下文中生成列表Haskell 过滤应用程序,haskell,Haskell,我正在学习NICTA Haskell课程,坚持学习应用程序的最后一部分,以下是我的学习进度 基本上,我们要用谓词过滤列表,该谓词在应用程序上下文中生成列表 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 :: 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
filtering::Applicative f=>(a->f Bool)->列表a->f(列表a)
过滤Nil=纯零
过滤f(x:.剩余)=
让整个=提升2(:)(纯x)(过滤f剩余)英寸
lift2滤波器(常数f x)整体
所以total
这里是完整的未过滤列表,所以我提升了一个filter
,在(const f x)
中添加的常量是为了满足filter
中的(a->Bool)
要求
一切正常,编译成功,但其中一个测试用例失败,所以这里一定出了问题
例如,filtering(Id.even)[4,5,6]
只返回Id[4]
,而不是Id[4,6]
(Id只是一个容器,是一个应用程序。)
我在什么地方出错了吗?这里的问题是(const f x)
。该函数将用于检查整个列表,但它只提供当前x
上的f
结果。这意味着当它处理[5,6]
子列表时,它本质上是过滤器(const False)[5,6]
,这会导致一个空列表
您不能调用filter
为您执行此操作,因为它的形状不正确。这样的事情总会发生。相反,只需关注是否包含当前元素,并让递归正确处理列表的其余部分。(我尽量不多说,因为这门课的目的当然是让你自己弄清楚。)这个怎么样(我用的是普通的列表,不是你的ctor)
我对你认为“完整未过滤列表”的必要性感到困惑,我想你也是。完整的未筛选列表将显示它
x:。休息
,你已经有了
显然,你的意思是“如果谓词总是产生True
,那么整个结果就是这样……但这没有任何意义,因为你基本上预测它总是产生True
您需要考虑的是,而不是这个代码>整< /Cord>东西,是两个应用性包装的值:谓词在头上的结果——即“代码> fx::f BOOL。并且,作为递归,列表中的过滤其余部分,<代码>过滤F REST::F(列表A)< /代码> ./P> 现在,正如您明显注意到的,
liftA2
通常是组合两个Applicative
包装的值的最简单方法(尽管
实际上倾向于使代码更整洁)
在这种情况下,
liftA2 :: (Bool->List a->List a) -> f Bool -> f (List a) -> f (List a)
因此,您需要一个函数
Bool->List a->List a
,如果布尔值为真,它会在x
前面加上前缀,否则列表就会保持原样。好吧,在本地块中定义它应该不是一个问题。如果不在一个函数中尝试,而是将它分解成更小的子块,那么这个问题就会简单得多问题
我要给出的第一个提示是,它看起来非常像类的问题,其中包括这个方法,它的签名应该让你去“嗯!“:
通过有趣的列表
输入您的问题,我推测您正在学习的课程不允许使用库函数来解决问题。我不太同意这种做法;我认为最好的学习方法是以下两步过程:
使用库函数解决问题。通过这种方式,您可以了解库,更重要的是,您可以了解如何将问题分解为更小、更通用的库
编写您自己使用的库函数版本。通过这种方式,您可以了解基于库的解决方案的工作原理
因此,我建议作为子问题,您应该尝试为您的列表
类型编写自己版本的遍历
下一个子问题:使用遍历
,编写此函数应该很简单:
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
一旦有了它,我要做的下一步就是编写这个函数:
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
,在它正常工作后,编写自己版本的函数作为额外练习。(事实上,考虑到问题中的非标准列表
类型,您几乎不得不编写自己的版本来以这种方式解决问题。)
一旦你掌握了所有这些,你就可以写:
filtering :: Applicative f => (a -> f Bool) -> List a -> f (List a)
filtering p xs = fmap removeFalse (tagWithBool xs)
请注意,这使用了fmap
,因此您必须为List
类型实现Functor
(或者只是一个mapList::(a->b)->List a->List b
函数).Id
是什么,过滤
与序列
有什么关系?@luqui如果你假设序列
应该是过滤
,Id
是平凡的应用程序
实例的构造函数,那么这个问题是有意义的。为什么你都在列表上递归,同时运行fil这两个操作都访问了整个列表,似乎给了你平方复杂度而不是linearTypo,对不起,伙计们……这应该是filtering@tohava我不知道如何将(:)
更改为如果f x那么(:)否则跳过
或类似的东西,因为Bool是在上下文中的。有什么建议吗?啊!没错。我如何在第一次提升中调整(:)
,以便跳过东西?这里的问题是Bool在context@bluebelle提示:获取可作为(:)输入的Bool
更换,也可以将(纯x)
(您不需要,因为您已经有x
作为参数可用)更改为(f x)
。这样就行了
filtering :: Applicative f => (a -> f Bool) -> List a -> f (List a)
filtering p xs = fmap removeFalse (tagWithBool xs)