Haskell 编写此函数(通常称为filterA)的惯用方法是什么?
我正在修这门课 这里有一个关于应用程序的部分,我被要求实现一个具有以下行为和类型的函数Haskell 编写此函数(通常称为filterA)的惯用方法是什么?,haskell,applicative,Haskell,Applicative,我正在修这门课 这里有一个关于应用程序的部分,我被要求实现一个具有以下行为和类型的函数 -- | Filter a list with a predicate that produces an effect. -- -- >>> filtering (ExactlyOne . even) (4 :. 5 :. 6 :. Nil) -- ExactlyOne [4,6] -- -- >>> filtering (\a -> if a > 13 the
-- | Filter a list with a predicate that produces an effect.
--
-- >>> filtering (ExactlyOne . even) (4 :. 5 :. 6 :. Nil)
-- ExactlyOne [4,6]
--
-- >>> filtering (\a -> if a > 13 then Empty else Full (a <= 7)) (4 :. 5 :. 6 :. Nil)
-- Full [4,5,6]
--
-- >>> filtering (\a -> if a > 13 then Empty else Full (a <= 7)) (4 :. 5 :. 6 :. 7 :. 8 :. 9 :. Nil)
-- Full [4,5,6,7]
--
-- >>> filtering (\a -> if a > 13 then Empty else Full (a <= 7)) (4 :. 5 :. 6 :. 13 :. 14 :. Nil)
-- Empty
--
-- >>> filtering (>) (4 :. 5 :. 6 :. 7 :. 8 :. 9 :. 10 :. 11 :. 12 :. Nil) 8
-- [9,10,11,12]
--
-- >>> filtering (const $ True :. True :. Nil) (1 :. 2 :. 3 :. Nil)
-- [[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3]]
filtering :: Applicative f => (a -> f Bool) -> List a -> f (List a)
——|使用产生效果的谓词筛选列表。
--
-->>>过滤(精确到一个偶数)(4.5.6.Nil)
--精确的里昂[4,6]
--
-->>>筛选(\a->如果a>13,则清空else Full)(a>>筛选(\a->如果a>13,则清空else Full)(a>>筛选(\a->如果a>13,则清空else Full)(a>>筛选(>)(4.5.6.7.8.9.10.11.12.Nil)8
-- [9,10,11,12]
--
-->>>筛选(常量$True:.True:.Nil)(1:.2:.3:.Nil)
-- [[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3]]
过滤::应用程序f=>(a->f布尔)->列表a->f(列表a)
我提出了以下实现,它满足了所有的要求
filtering f as =
let x = sequence (f `map` as)
y = zip as <$> x
z = filter snd <$> y
in map fst <$> z
将f过滤为=
设x=序列(f`map`as)
y=压缩为x
z=滤波器snd y
在地图fst z中
但我觉得这有点“迂回”,我想不出更直接的方法来做
<>注释:我已经扩展到<代码> x,y,z ,因为它使我更容易跟随正在发生的事情,并且当我意识到我可以把它全部表达在一行上时,我不认为这是更直接的,因此不能回答我的问题。
注2:本课程似乎是从基本部分构建通用类型类。我们从
List
的自定义实现开始,接着是Functor
,现在是Applicative
,因此我只能使用这些类中的概念。我还不能使用Monad
中的任何内容。我的第一个想法是要以普通的过滤器开始,请执行以下操作:
filter :: (a -> Bool) -> List a -> List a
filter _ Nil = Nil
filter f (x :. xs) =
let b = f x
ys = filter f xs
in
if b then x :. ys else ys
…并尝试将其扩展到Applicative
:
filtering :: (Applicative f) => (a -> f Bool) -> List a -> f (List a)
filtering _ Nil = pure Nil
filtering f (x :. xs) =
let b = f x
ys = filtering f xs
in
if b then x :. ys else ys
这种尝试有两个问题:fx
是fbool
,而不是Bool
,因此如果b那么…
是类型错误,过滤fxs
是f(列表a)
,而不是列表a
,因此x:.ys
是类型错误
我们可以使用lift2::(Applicative f)=>(a->b->c)->fa->fb->fc来解决这些问题:
filtering f (x :. xs) =
lift2 (\b ys -> if b then x :. ys else ys) (f x) (filtering f xs)
lift2
让我们分别从fx
和过滤fxs
局部提取Bool
和列表a
;或者更准确地说,我们将if…then…else
计算包装在一个函数中,该函数lift2
然后推入f
或者,我们可以直接使用
和
:
filtering f (x :. xs) =
(\b ys -> if b then x :. ys else ys) <$> f x <*> filtering f xs
过滤f(x:.xs)=
(\b ys->if b then x:.ys else ys)f x筛选f xs
或者以稍微不同的方式编写助手函数:
filtering f (x :. xs) =
(\b -> if b then (x :.) else id) <$> f x <*> filtering f xs
过滤f(x:.xs)=
(\b->if b then(x:.)else id)f x筛选f xs
这里是一个关于foldr
的实现(并且是使用基本类型和函数编写的)。我相当肯定它相当于
以下是一个不同的例子,灵感来自您的原始解决方案(注意
序列(map f xs)
与遍历f xs
相同):
filterA::Applicative f=>(a->f Bool)->[a]->f[a]
filterA f=fmap concat.transverse(\x->bool[][x]f x)
(bool Nothing(Just x)
和catMaybes
来自数据。可能
而不是bool[][x]
和concat
也会起作用。)
请注意,此解决方案需要对列表进行额外的遍历,因为
遍历
不足以实现筛选。这就是为什么筛选,catMaybes
,filterA
和朋友需要进行泛化的原因。旁注:witherable库确实调用(一个概括的版本)它。回答得很好!对于所有不熟悉本课程的阅读者来说,自定义列表类型上的运算符(:)相当于股票列表类型上的(:)运算符。@ChechyLevas和lift2
是liftA2
fromControl.Applicative
。
import Control.Applicative (liftA2)
import Data.Bool (bool)
filterA :: Applicative f => (a -> f Bool) -> [a] -> f [a]
filterA f = foldr (\x xs -> liftA2 (++) (bool [] [x] <$> f x) xs) (pure [])
GHCi> filterA (\x -> print x *> pure (x > 5)) [1..10]
1
2
3
4
5
6
7
8
9
10
[6,7,8,9,10]
filterA :: Applicative f => (a -> f Bool) -> [a] -> f [a]
filterA f = fmap concat . traverse (\x -> bool [] [x] <$> f x)