在Haskell中为布尔函数执行`and`和`or`

我只写了以下两个函数:

fand :: (a -> Bool) -> (a -> Bool) -> a -> Bool fand f1 f2 x = (f1 x) && (f2 x) f_or :: (a -> Bool) -> (a -> Bool) -> a -> Bool f_or f1 f2 x = (f1 x) || (f2 x) 

它们可能被用来组合两个布尔函数的值,例如:

 import Text.ParserCombinators.Parsec import Data.Char nameChar = satisfy (isLetter `f_or` isDigit) 

看了这两个函数之后,我意识到它们非常有用。 以至于我现在怀疑它们被包含在标准库中,或者更可能是使用现有函数来执行此操作的干净方式。

什么是“正确”的方式来做到这一点?

一个简化,

 f_and = liftM2 (&&) f_or = liftM2 (||) 

要么

  = liftA2 (&&) = liftA2 (||) 

((->) r)应用函数中。


适用版本

为什么? 我们有:

 instance Applicative ((->) a) where (<*>) fgx = fx (gx) liftA2 fab = f <$> a <*> b (<$>) = fmap instance Functor ((->) r) where fmap = (.) 

所以:

  \fg -> liftA2 (&&) fg = \fg -> (&&) <$> f <*> g -- defn of liftA2 = \fg -> ((&&) . f) <*> g -- defn of <$> = \fgx -> (((&&) . f) x) (gx) -- defn of <*> - (.) fg = \x -> f (gx) = \fgx -> ((&&) (fx)) (gx) -- defn of (.) = \fgx -> (fx) && (gx) -- infix (&&) 

Monad版本

或者对于liftM2 ,我们有:

 instance Monad ((->) r) where return = const f >>= k = \ r -> k (fr) r 

所以:

  \fg -> liftM2 (&&) fg = \fg -> do { x1 <- f; x2 <- g; return ((&&) x1 x2) } -- defn of liftM2 = \fg -> f >>= \x1 -> g >>= \x2 -> return ((&&) x1 x2) -- by do notation = \fg -> (\r -> (\x1 -> g >>= \x2 -> return ((&&) x1 x2)) (fr) r) -- defn of (>>=) = \fg -> (\r -> (\x1 -> g >>= \x2 -> const ((&&) x1 x2)) (fr) r) -- defn of return = \fg -> (\r -> (\x1 -> (\r -> (\x2 -> const ((&&) x1 x2)) (gr) r)) (fr) r) -- defn of (>>=) = \fgx -> (\r -> (\x2 -> const ((&&) (fx) x2)) (gr) r) x -- beta reduce = \fgx -> (\x2 -> const ((&&) (fx) x2)) (gx) x -- beta reduce = \fgx -> const ((&&) (fx) (gx)) x -- beta reduce = \fgx -> ((&&) (fx) (gx)) -- defn of const = \fgx -> (fx) && (gx) -- inline (&&) 

如果你总是需要两个函数,那就更丑陋了,但我想我会推广它:

 mapAp fs x = map ($x) fs fAnd fs = and . mapAp fs fOr fs = or . mapAp fs > fOr [(>2), (<0), (== 1.1)] 1.1 True > fOr [(>2), (<0), (== 1.1)] 1.2 False > fOr [(>2), (<0), (== 1.1)] 4 True 

完全撕下了TomMD,我看到了and . map and . mapor . map or . map ,忍不住想调整一下:

 fAnd fs x = all ($x) fs fOr fs x = any ($x) fs 

我认为这些阅读很好。 fAnd :列表中的所有函数是否将x应用于它们时为TruefOr :列表中的任何函数是否应用xTrue

 ghci> fAnd [even, odd] 3 False ghci> fOr [even, odd] 3 True 

但是,这是一个奇怪的名字select。 肯定是一个很好的扔这些命令程序员的循环 。 =)

在Don的说法之上, liftA2/liftM2版本可能不够懒惰:

> let a .&&. b = liftA2 (&&) ab in pure False .&&. undefined let a .&&. b = liftA2 (&&) ab in pure False .&&. undefined

*** Exception: Prelude.undefined

Woops!

所以,你可能需要一个稍微不同的function。 请注意,这个新function需要Monad约束 – Applicative是不够的。

> let a *&&* b = a >>= \a' -> if a' then b else return a' in pure False *&&* undefined

False

好多了

至于表示on函数的答案,这是针对函数相同但参数不同的答案。 在你的情况下,function是不同的,但论据是相同的。 这是你的例子改变,以便on一个合适的答案:

(fx) && (fy)

可以这样写:

on (&&) fxy

PS:括号是不必要的。

这也可以使用箭头来完成:

 import Control.Arrow ((&&&), (>>>), Arrow(..)) split_combine :: Arrow cat => cat (b, c) d -> cat ab -> cat ac -> cat ad split_combine hfg = (f &&& g) >>> h letter_or_digit = split_combine (uncurry (||)) isLetter isDigit 

&&& (与&&无关)拆分input; >>>是箭头/类别组合。

这是一个例子:

 > map letter_or_digit "aQ_%8" [True,True,False,False,True] 

这是有效的,因为函数 – -> – 是类别和箭头的实例。 比较types签名与唐的liftA2liftM2例子显示了相似之处:

 > :t split_combine split_combine :: Arrow cat => cat (b, c) d -> cat ab -> cat ac -> cat ad > :t liftA2 liftA2 :: Applicative f => (b -> c -> d) -> fb -> fc -> fd 

除了currying,注意你几乎可以把第一个types转换成第二个types,用cat a ---> fArrow ---> Applicative来代替(另一个区别是split_combine并不局限于在第一个函数中使用纯函数参数;虽然可能不重要)。

这已经被提及,但是以更复杂的方式提到。 你可以使用应用的东西。

对于函数来说,它所做的基本function是将相同的parameter passing给一些可以在最后结合使用的函数。

所以你可以像这样实现它:

 (&&) <$> aCheckOnA <*> anotherCheckOnA $ a 

对于链中的每个<*> ,您将得到另一个适用于a的函数,然后使用交替写入为<$> fmap所有输出混合在一起。 与&&工作的原因是因为它需要两个参数,而且我们有两个函数一起出演。 如果在那里有一个额外的星星和另一个支票,你必须写下类似的东西:

 (\abc -> a && b && c) <$> aCheckOnA <*> anotherCheckOnA <*> ohNoNotAnotherCheckOnA $ a 

检查这个更多的例子

如果f1和f2是相同的,那么你可以使用'on':

 on :: (b -> b -> c) -> (a -> b) -> a -> a -> c 

在Data.Function中

 fand1 f = (&&) `on` f for1 f = (||) `on` f 

典型用法:

 Data.List.sortBy (compare `on` fst) 

(来自Hoogle )