Bush
は「非正規」または「非均一」データ型です。つまり、再帰的な場合、指定された型引数と同じ型引数を使用しません。これらの理由を説明するのは難しい場合がありますが、この場合、答えは思ったより簡単です。
data Bush a = BEmpty | BCons a (Bush (Bush a))
instance Eq a => Eq (Bush a) where
BEmpty == BEmpty = True
BCons x xs == BCons y ys = x == y && xs == ys
_ == _ = False
それでおしまい!(==)
自分自身を再帰的に呼び出すだけで、完了です。
しかし、ちょっと待ってください。ここで少し汚いトリックを使っています。Eq
型クラス メカニズムを使用しています。
型クラスがまったくない場合、どうすればよいでしょうか? Eq a =>
型クラスがなければ、そもそも制約を使用できませんでした。代わりに、明示的な比較関数を渡す場合があります:: a -> a -> Bool
。それを念頭に置いて、非常によく似たコードを書くことができます。
eqBush :: (a -> a -> Bool) -> Bush a -> Bush a -> Bool
eqBush _ BEmpty BEmpty = True
eqBush eqA (BCons x xs) (BCons y ys) = eqA x y && eqBush (eqBush eqA) xs ys
eqBush _ _ _ = False
Bush a
各再帰呼び出しでは、取得したのと同じ比較関数を渡しているわけではありません。sではなく sを比較する比較関数を渡していますa
! これは、より明示的であることを除いて、型クラスで発生するのと実際には同じことです。再帰呼び出しの構造がデータ型定義の構造と同じであることに注意してBush (Bush a)
くださいeqBush (eqBush eqA)
。
この型に対する他の再帰的な定義でも同じことが起こります。これは便利なものです(これは本当にのfmap
ためのものです):Bush
mapBush :: (a -> b) -> Bush a -> Bush b
mapBush _ BEmpty = BEmpty
mapBush f (BCons x xs) = BCons (f x) (mapBush (mapBush f) xs)
sumBush
これにより、次のような関数を簡単に記述できます。
sumBush :: Bush Int -> Int
sumBush BEmpty = 0
sumBush (BCons x xs) = x + sumBush (mapBush sumBush xs)
この種の再帰は多相再帰と呼ばれます。これは、多相関数が呼び出された型とは異なる型で自分自身を呼び出すためです。ポリモーフィック再帰は、理解するのが難しい場合があります。実際、その型推論は (一般的に) 決定不可能であるため、(一般的に) 独自の型シグネチャを作成する必要があります。より自然に。Bush
感触をつかむために、他の関数をいくつか書いてみてください。
以下は、いくつかのコードを書いてみるための他のいくつかの非正規データ型です。
data Tree a = Leaf a | Branch (Tree (a,a))
-- 完全な二分木。
data
FunList
a b = Done b | More a (FunList a (a -> b))
-- s のリストa
と、正確にその数の s を取り、 aa
を返す関数b
(これはTraversalsに関連しています)。