我知道可以这样做:
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]
ETC?
我知道可以这样做:
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]
ETC?
我相信您想检查是否有任何元素同时是(<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]
您的符号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]
另一种方法是使用Monoid
s。Bool
是一个Monoid
当包裹在All
或Any
从Data.Monoid
。我们需要它,因为有两种组合方式[Bool]
- 我们可以使用&&
或||
。这就是为什么存在类型All :: Bool -> All
和Any :: 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
然后搜索http://www.haskell.org/hoogle/(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
您还可以通过使用一些实例来定义一个接受谓词列表的运算符,Monoid
如下所示。
test = any (andP [(>3),(<5)]) [1,2,3,4,5]
andP :: [a -> Bool] -> a -> Bool
andP ps = getAll . mconcat (map (All.) ps)