実際にはnot
ブール値の関数がありますが、いつものように型を正しくする必要があります。既存の関数が次のタイプであるとします。
ascending :: (Ord a) => [a] -> Bool
ascending (x1:x2:xs) = x1 <= x2 && ascending (x2:xs)
ascending _ = True
descending :: (Ord a) => [a] -> Bool
descending (x1:x2:xs) = x1 >= x2 && descending (x2:xs)
descending _ = True
両方を必要とするということは、リストが等しくなければならないことを意味します。これは、上記で定義した意味で、リストを昇順と降順の両方にする唯一の方法だからです。
both xs = ascending xs && descending xs
ブール値を反転するには、次のnot
関数があります。
not :: Bool -> Bool
次に、どちらでもないことは、この関数で表現されます。
neither xs = not (ascending xs || descending xs)
もちろん、これは次のようになります。
neither xs = not (ascending xs) && not (descending xs)
リーダーファンクターでアプリケーションスタイルを使用して、これをもう少し見栄えよくすることができます。
import Control.Applicative
both = liftA2 (&&) ascending descending
neither = not . liftA2 (||) ascending descending
または代わりに:
neither = liftA2 (&&) (not . ascending) (not . descending)
詳細:これにより、述語の概念が生じます。
type Predicate a = a -> Bool
述語はブール関数です。ascending
上記で定義された2つの関数descending
は述語です。ブール値を反転する代わりに、述語を反転できます。
notP :: Predicate a -> Predicate a
notP = (not .)
そして、ブール値の接続詞と論理和の代わりに、述語にそれらを含めることができます。これにより、複合述語をより適切に記述できます。
(^&^) :: Predicate a -> Predicate a -> Predicate a
(^&^) = liftA2 (&&)
(^|^) :: Predicate a -> Predicate a -> Predicate a
(^|^) = liftA2 (||)
これにより、私たちは書くことができboth
、neither
本当にうまくいきます:
both = ascending ^&^ descending
neither = notP ascending ^&^ notP descending
述語には次の法則が適用されます。
notP a ^&^ notP b = notP (a ^|^ b)
neither
だから私たちはさらにうまく書き直すことができます:
neither = notP (ascending ^|^ descending)