在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