给定以下过滤函数作为一元 predicate ,
f1 :: Int -> Bool
f1 x = x > 30
f2 :: Int -> Bool
f2 x = x < 60
f3 :: Int -> Bool
f3 x = x `mod` 3 == 0
我想通过所有的整数来过滤一个整数列表。目前我正在做的事情大致如下:
filtered = filter f1 $ filter f2 $ filter f3 [1..90]
-- [33,36,39,42,45,48,51,54,57]
但这似乎不是最好的解决方案;尤其是我不喜欢filter
的多次重复和缺乏可组合性。
是否有一种方法可以将所有这些 predicate 组合成一个 predicate ,让我们将其命名为<?>
,这样可能的语法将类似于以下内容?
filtered = filter (f1 <?> f2 <?> f3) [1..90]
-- [33,36,39,42,45,48,51,54,57]
这个假设的<?>
运算符的类型签名将是(a -> Bool) -> (a -> Bool) -> (a -> Bool)
,但在Hoogle上是I wasn't able to find any such thing。
6条答案
按热度按时间uqzxnwby1#
这个呢?
这里,将
&&
提升为Applicative
得到了标记为<?>
的结果,即,将两个一元 predicate 的结果“与”在一起的运算符。我最初使用
.&&.
作为提升运算符,但amalloy建议,通过与其他Functor
/Applicative
提升运算符(如<$>
*)进行类比,<&&>
将是一个更好的名称 *。kzmpq1sx2#
本质上,上面的工作是因为
sequence
(在上面使用的(->) a
单子上)接受一个函数列表,并返回一个返回列表的函数。使用
and :: [Bool] -> Bool
进行后期合成会得到一个布尔结果,因此可以在filter
中使用它。而且,有观点也没什么好羞愧的:
只是稍微长了一点,而且可以说读起来更简单。
h7appiyu3#
Data.Monoid
定义了一个Predicate
类型,可用于表示您的函数:Predicate
具有Semigroup
示例,该示例将两个 predicate 组合成一个 predicate ,如果 * 两个 * 输入 predicate 都满足,则满足该 predicate 。不幸的是,在将组合 predicate 与
filter
一起使用之前,您需要将它们展开。您可以定义自己的filterP
函数,并使用它来代替filter
:还有一个
Monoid
示例(标识是一个 predicate ,它总是返回True
),您可以像这样使用它你可以把它重新分解成
zzlelutf4#
您可以使用
extra
package的**(&&^) :: Monad m => m Bool -> m Bool -> m Bool
**:这给了我们:
(&&^)
函数实现为[src]:这是因为函数类型是
Monad
:因此,这意味着
ifM
被实现为如下函数:(&&^)
函数检查第一个条件b x
是否为True
,如果不是,则返回False
(因为f
为const False
,因此f x
为False
)。如果b x
为True
,则检查链中的下一个元素。niknxzdl5#
我们需要一种方法来使用像
and
这样的函数来组合 predicate ,而不仅仅是布尔值。一种懒惰的方法是向Hoogle请求
Functor f => ([b]-> b) -> [f b] -> f b
这样的类型签名,其中f大概是Int ->
这样的东西。看起来效果不错:
正在检查:
nzk0hqpo6#
其他的答案都很好,但是我会给予我喜欢的合并函数的方式,它非常简洁。我非常喜欢使用Control中的lift函数。Monad
liftM2的工作方式是将(&&)函数提升为一个单子,并将f1和f2作为参数。
我知道有一个名为liftM3的函数,但我不确定它是否能在此上下文中工作。
https://hackage.haskell.org/package/base-4.14.0.0/docs/Control-Monad.html#v:liftM3