スキーム アプリケーションから関数を返すことができるため、単一の引数に作用する再帰スキームで十分です。この場合、Expr -> Bool
上のスキーム アプリケーションから関数を返すことができますExpr
。効率的な等価性チェックには、パラモーフィズムのみが必要です。
{-# language DeriveFunctor, LambdaCase #-}
newtype Fix f = Fix (f (Fix f))
data ExprF r = Const Int | Add r r deriving (Functor, Show)
type Expr = Fix ExprF
cata :: Functor f => (f a -> a) -> Fix f -> a
cata f = go where go (Fix ff) = f (go <$> ff)
para :: Functor f => (f (Fix f, a) -> a) -> Fix f -> a
para f (Fix ff) = f ((\x -> (x, para f x)) <$> ff)
eqExpr :: Expr -> Expr -> Bool
eqExpr = cata $ \case
Const i -> cata $ \case
Const i' -> i == i'
_ -> False
Add a b -> para $ \case
Add a' b' -> a (fst a') && b (fst b')
_ -> False
もちろん、次cata
の点で簡単に実装できますpara
。
cata' :: Functor f => (f a -> a) -> Fix f -> a
cata' f = para (\ffa -> f (snd <$> ffa)
技術的には、ほとんどすべての便利な関数は を使用して実装cata
できますが、必ずしも効率的であるとは限りません。para
以下を使用して実装できますcata
。
para' :: Functor f => (f (Fix f, a) -> a) -> Fix f -> a
para' f = snd . cata (\ffa -> (Fix (fst <$> ffa) , f ffa))
ただし、para'
inを使用すると、入力のサイズが常に線形であるeqExpr
ため、2 次の複雑さが得られますが、 を使用して、一定時間で最上位の値を覗くことができます。para'
para
Expr