多个元素的布尔运算符

时间:2012-09-29 15:33:25

标签: haskell

我知道可以做到:

any (>3) [1,2,3,4,5]

但是优雅的实施方式是什么:

any and[(>3),(<5)] [1,2,3,4,5]

all or[(<2),(>4)] [1,2,3,4,5]

等?

4 个答案:

答案 0 :(得分:10)

我相信你想检查是否有(<5)(>3)的任何元素。

你可以这样做:

any (\x -> x > 3 && x < 5) [1..5]

你的另一个可以通过

来完成
any (\x -> x < 2 || x > 4) [1..5]

但是,定义&&||来处理函数可能会更有趣:

infixr 3 &&&
infixr 3 |||

(&&&) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
(f &&& g) x = f x && g x

(|||) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
(f ||| g) x = f x || g x

现在我们可以将您的示例重写为:

any ((>3) &&& (<5)) [1..5]
any ((<2) ||| (>4)) [1..5]

答案 1 :(得分:6)

您的符号and[(>3),(<5)]几乎可以直接作为高阶函数实现。我将其称为andP,因为any采用谓词和值列表,我们需要一个带有谓词列表的函数。

andP :: [a -> Bool] -> a -> Bool
andP ps x = all ($ x) ps

现在

andP [(>3), (<5)] x = x > 3 && x < 5

您可以像在

中一样在初始请求中书写
any (andP [(>3), (<5)]) [1,2,3,4,5]

作为旁注,对于这个特殊的例子,我认为更明确的方式是:

between :: (Ord a) => a -> a -> a -> Bool
between lo hi x = lo < x && x < hi

any (between 3 5) [1,2,3,4,5]

答案 2 :(得分:3)

另一种方法是使用Monoid s。 Bool Monoid包含在AllAnyData.Monoid。我们需要这样做,因为有两种方法可以合并[Bool] - 我们可以使用&&||。这就是为什么All :: Bool -> AllAny :: Bool -> Any类型是Monoid的实例。例如:

> import Data.Monoid
> getAll $ mconcat [All True, All True, All False]
False
> getAll $ mconcat [All True, All True, All True]
True
> getAny $ mconcat [Any True, Any True, Any False]
True

我们使用的另一个事实是函数的Monoid实例(同样来自Data.Monoid):

instance Monoid b => Monoid (a -> b) where
        mempty _ = mempty
        mappend f g x = f x `mappend` g x

现在我们可以附加功能:

> :t All
All :: Bool -> All
> :t (<5)
(<5) :: (Num a, Ord a) => a -> Bool
> :t All . (<5)
All . (<5) :: (Num a, Ord a) => a -> All
> :t ((All . (<5)) <> (All . (>3)))
((All . (<5)) <> (All . (>3))) :: (Num a, Ord a) => a -> All
> getAll $ ((All . (<5)) <> (All . (>3))) 4
True

将其概括为函数列表:

> getAll $ mconcat [(All. (<5)), (All . (>3))] $ 4
True
> getAll $ mconcat (map (All .) [(<5), (>3)]) $ 4
True

然后在(a->b) -> [a] -> b搜索foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m,我们看到mconcat . map我们可以使用> import Data.Foldable > getAll $ foldMap (All .) [(<5), (>3)] $ 4 True 代替> map (getAll . foldMap (All .) [(<5), (>3)]) $ [1..5] [False,False,False,True,False] > Prelude.or $ map (getAll . foldMap (All .) [(<5), (>3)]) $ [1..5] True

{{1}}

最后将其映射到数字列表:

{{1}}

答案 3 :(得分:2)

您还可以通过使用一些Monoid实例来定义一个获取谓词列表的运算符,如下所示。

test = any (andP [(>3),(<5)]) [1,2,3,4,5]

andP :: [a -> Bool] -> a -> Bool
andP ps = getAll . mconcat (map (All.) ps)