44

次の2つの関数を書きました:

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)

これらは、次のような 2 つのブール関数の値を結合するために使用できます。

import Text.ParserCombinators.Parsec
import Data.Char

nameChar = satisfy (isLetter `f_or` isDigit)

この 2 つの機能を見て、非常に便利であることがわかりました。そのため、標準ライブラリに含まれているか、既存の関数を使用してこれを行うためのクリーンな方法がある可能性が高いと思われます。

これを行う「正しい」方法は何でしたか?

4

7 に答える 7

49

単純化すると、

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 (&&)
于 2011-04-18T23:53:48.957 に答える
9

TomMDand . mapを完全に盗用して、 andを見て、or . map微調整せずにはいられませんでした:

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

これらはうまく読めると思います。fAnd: を適用すると、すべての関数がリストTruex含まれますか? : が適用されたときfOrにリストに関数はありますか?Truex

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

ただし、fOr は奇妙な名前の選択です。確かに、これらの命令型プログラマーをループに投げ込むのに適したものです。=)

于 2011-04-19T01:33:56.430 に答える
7

常に2つの関数が必要な場合は醜いですが、一般化すると思います:

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
于 2011-04-18T23:59:43.903 に答える
3

ドンが言ったことに加えて、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

その方がいいです。

関数を示唆する答えについてはon、これは関数が同じで引数が異なる場合です。あなたのケースでは、関数は異なりますが、引数は同じです。onこれが適切な答えになるように変更された例です。

(f x) && (f y)

これは次のように記述できます。

on (&&) f x y

PS: 括弧は不要です。

于 2011-04-19T10:23:30.777 に答える
1

これは、矢印を使用して実行することもできます。

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の例liftA2liftM2例と比較すると、類似点がわかります。

> :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カリー化に加えて、 andを代入することで、最初のタイプを2番目のタイプにほぼ変換できることに注意してくださいArrow ---> Applicative(他の違いは、最初のsplit_combine引数で純粋関数を使用することに限定されないことです。おそらく重要ではありません)。

于 2012-11-28T15:38:39.440 に答える
-1

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)

(フーグルより)

于 2011-04-19T00:00:27.967 に答える