在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
    (<*>) f g x = f x (g x)

liftA2 f a b = f <$> a <*> b

(<$>) = fmap

instance Functor ((->) r) where
    fmap = (.)
所以:
  \\f g -> liftA2 (&&) f g
= \\f g -> (&&) <$> f <*> g          -- defn of liftA2
= \\f g -> ((&&) . f) <*> g          -- defn of <$>
= \\f g x -> (((&&) . f) x) (g x)    -- defn of <*> - (.) f g = \\x -> f (g x)
= \\f g x -> ((&&) (f x)) (g x)      -- defn of (.)
= \\f g x -> (f x) && (g x)          -- infix (&&)
单声道版本 或者对于
liftM2
,我们有:
instance Monad ((->) r) where
    return = const
    f >>= k = \\ r -> k (f r) r
所以:
  \\f g -> liftM2 (&&) f g
= \\f g -> do { x1 <- f; x2 <- g; return ((&&) x1 x2) }               -- defn of liftM2
= \\f g -> f >>= \\x1 -> g >>= \\x2 -> return ((&&) x1 x2)              -- by do notation
= \\f g -> (\\r -> (\\x1 -> g >>= \\x2 -> return ((&&) x1 x2)) (f r) r)  -- defn of (>>=)
= \\f g -> (\\r -> (\\x1 -> g >>= \\x2 -> const ((&&) x1 x2)) (f r) r)   -- defn of return
= \\f g -> (\\r -> (\\x1 ->
               (\\r -> (\\x2 -> const ((&&) x1 x2)) (g r) r)) (f r) r) -- defn of (>>=)
= \\f g x -> (\\r -> (\\x2 -> const ((&&) (f x) x2)) (g r) r) x         -- beta reduce
= \\f g x -> (\\x2 -> const ((&&) (f x) x2)) (g x) x                   -- beta reduce
= \\f g x -> const ((&&) (f x) (g x)) x                               -- beta reduce
= \\f g x -> ((&&) (f x) (g x))                                       -- defn of const
= \\f g x -> (f x) && (g x)                                           -- inline (&&)
完全剥夺了TomMD,我看到了
and . map
or . map
,不由得想要调整一下:
fAnd fs x = all ($x) fs
fOr fs x = any ($x) fs
我认为这些读起来很好。
fAnd
:对它们应用
x
后,列表functions14ѭ中的所有功能是否都存在?
fOr
:将ѭ15应用于to14ѭ列表中的功能吗?
ghci> fAnd [even, odd] 3
False
ghci> fOr [even, odd] 3
True
但是,fOr是一个奇怪的名字选择。当然,这是使那些命令式程序员陷入困境的好人。 =)
如果您总是想要两个函数,这很丑陋,但是我想我可以将其概括化:
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
在Don所说的之上,
liftA2/liftM2
版本可能还不够懒: >
let a .&&. b = liftA2 (&&) a b in pure False .&&. undefined
*** Exception: Prelude.undefined
哇! 因此,您可能想要一个稍微不同的功能。请注意,此新功能需要
Monad
约束-
Applicative
是不够的。 >
let a *&&* b = a >>= \\a\' -> if a\' then b else return a\' in pure False *&&* undefined
False
更好。 至于建议“ 28”功能的答案,这是针对功能相同但参数不同的情况。在给定的情况下,功能不同,但参数相同。这是您的示例经过更改,以使
on
是一个适当的答案:
(f x) && (f y)
可以这样写:
on (&&) f x y
PS:括号是不必要的。
这也可以使用箭头来完成:
import Control.Arrow ((&&&), (>>>), Arrow(..))

split_combine :: Arrow cat => cat (b, c) d -> cat a b -> cat a c -> cat a d
split_combine h f g = (f &&& g) >>> h

letter_or_digit = split_combine (uncurry (||)) isLetter isDigit
&&&
(与
&&
无关)分割输入;
>>>
是箭头/类别组成。 这是一个例子:
> map letter_or_digit \"aQ_%8\"
[True,True,False,False,True]
之所以可行,是因为函数
->
是Category和Arrow的实例。将类型签名与Don's的
liftA2
liftM2
示例进行比较,可以发现相似之处:
> :t split_combine 
split_combine :: Arrow cat => cat (b, c) d  -> cat a b -> cat a c -> cat a d

> :t liftA2
liftA2    :: Applicative f => (b -> c -> d) ->     f b ->     f c ->     f d
除了循环,还可以用
cat a ---> f
Arrow ---> Applicative
几乎将第一种转换成第二种(另一个区别是
split_combine
不仅限于在其第一个参数中采用纯函数;虽然可能并不重要)。
已经提到过这种方式,但是以更复杂的方式。您可以使用适用性的东西。 对于函数,基本上,它是将相同的参数传递给可以在最后合并的多个函数。 因此,您可以像这样实现它:
(&&) <$> aCheckOnA <*> anotherCheckOnA $ a
对于链中的每个
<*>
,您将获得另一个适用于a的函数,然后使用
fmap
或ly47ѭ将所有输出汇总在一起。之所以适用于
&&
,是因为它需要两个参数,并且我们将两个函数一起加注星号。如果那里有多余的星星,而另外一张支票,则您必须写出以下内容:
(\\a b c -> 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)

要回复问题请先登录注册