在Haskell中,对布尔函数执行`和`和`或'

在Haskell中,对布尔函数执行`和`和`或',haskell,combinators,pointfree,Haskell,Combinators,Pointfree,我刚刚编写了以下两个函数: 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.Pars

我刚刚编写了以下两个函数:

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 (||)

((->)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->liftM2(&&)f g
=\f g->do{x1>=\x1->g>=\x2->return(&&&)x1x2--by do表示法
=\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)--返回的定义
=\f g->(\r->)(\x1->
(\r->(\x2->const(&&&)x1x2))(gr)r))(fr)r--defn of(>>=)
=\f g x->(\r->(\x2->const(&&&)(f x)x2))(g r)r)x--beta reduce
=\fGx->(\x2->const(&&&)(fx)x2))(gx)x--beta-reduce
=\f g x->const(&&)(f x)(g x))x--beta reduce
=\f g x->((&&)(f x)(g x))--常数的定义
=\f g x->(f x)和(g x)--内联(&&)

如果你总是想要两个函数,那就更难看了,但我想我应该概括一下:

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
mapapfs x=map($x)fs
fAnd fs=和.mapAp fs
对于fs=或.mapAp fs

>对于[(>2),[(>2),[(>2),(如果f1和f2相同,则可以使用“on”:

on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
在基础数据中。函数

fand1 f = (&&) `on` f
for1 f = (||) `on` f
典型用法:

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

完全脱离了TomMD,我看到了
和.map
或.map
,忍不住想要调整它:

fAnd fs x = all ($x) fs
fOr fs x = any ($x) fs
我认为这些都读得很好。
fAnd
:当
x
应用于它们时,列表中的所有函数都是
True
吗?
fOr
:当
x
应用于它们时,列表中的任何函数都是
True

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

不过,fOr是一个奇怪的名称选择。当然,这是一个很好的名称,可以让那些命令式程序员循环。=)

除了Don所说的以外,
liftA2/liftM2
版本可能还不够懒:

>
设a.&.b=liftA2(&&)a b为纯假。&&.未定义

***例外:前奏。未定义

呜呜

因此,您可能需要一个稍微不同的函数。请注意,这个新函数需要一个
Monad
约束--
Applicative
是不够的

>
让a*&&*b=a>=\a'->如果a'那么b else返回a',为纯False*&&*undefined

False

那更好

对于建议使用
on
函数的答案,这适用于函数相同但参数不同的情况。在您给定的情况下,函数不同但参数相同。下面是您修改的示例,以便
on
是一个合适的答案:

(fx)和&(fy)

可以这样写:

on(&&)f x y


注:括号是不必要的。

这也可以通过以下方法完成:

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

下面是一个例子:

> map letter_or_digit "aQ_%8"
[True,True,False,False,True]
这是因为函数--
->
--是Category和Arrow的实例。将类型签名与Don的
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

除了curry之外,请注意,通过替换
cata-->f
Arrow-->Applicative
,您几乎可以将第一种类型转换为第二种类型(另一个区别是
split\u combine
不限于在其第一个参数中使用纯函数;但可能并不重要).

这已经提到了很多,但用了一种更复杂的方式。你可以使用应用性的东西

对于函数,它的基本功能是将相同的参数传递给许多函数,这些函数可以在最后合并

所以您可以这样实现它:

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

有关更多示例,请参见Haskell,这是一种语言,其中所有内容都已存在于某个标准库中的a类型类中。Typeclassopedia可以帮助解决这一问题。这与问题的目的相反-它针对多个值测试一个函数(已由
all
any
有效地处理),而不是针对一个值的多个函数。啊,是的,更好的实现。我怎么能忽略
all
any
?!fAnd=fmap和.sequence fOr=fmap或.sequences我不确定
fAnd[偶数,奇数]3
all($3)[偶数,奇数]
尽管如此。无点版本:
过滤器(`any`[偶数,prime])。翻转($)xs
看到它在
Monad
实例中工作有点棘手,但很高兴能完成它。我的第一个猜测是
上…得到了一种倒退。Monad实例的派生对你来说也是吗,AdameExcellent,尽管我发现从下到上更容易阅读。如果你使用
liftA2而不是
liftM2
。这是有效的:
让a.&&.b=liftA2(&&&)a b进入(纯假.&&&.未定义)(
@SjoerdVisscher为什么它对((->)e懒惰)或者读卡器单子,而不是IO单子?@JohannesGerer在
fa
f
中的副作用必须在
a
中的副作用之前发生。但是读卡器单子没有副作用,因此它可以安全地忽略不需要结果的计算。这是我第一次看到
($x)
用于Haskell。在那里的一瞬间,我感觉自己又回到了PH
ghci> fAnd [even, odd] 3
False
ghci> fOr [even, odd] 3
True
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]
> :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
(&&) <$> aCheckOnA <*> anotherCheckOnA $ a
(\a b c -> a && b && c) <$> aCheckOnA <*> anotherCheckOnA <*> ohNoNotAnotherCheckOnA $ a