これをエンコードする良い方法は、Data.Foldable によって提供されるトラバーサルに頼ることです。
{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Data.Foldable
import Data.Monoid
拡張機能を使用してそのインスタンスを自動的に派生させることができますが、Node コンストラクターのフィールドを並べ替えて、順序通りのトラバーサルを提供する必要があります。
その一方で、データ型自体の制約を排除する必要があります。それらは実際には何の利益ももたらさず、Haskell 2011 の時点で言語から削除されました (このような制約を使用する場合は、データ型ではなく、クラスのインスタンスに配置する必要があります)。
data BST a
= Void
| Node
{ left :: BST a
, val :: a
, right :: BST a
} deriving (Eq, Ord, Read, Show, Foldable)
最初に、リストが厳密にソートされるとはどういう意味かを定義します。
sorted :: Ord a => [a] -> Bool
sorted [] = True
sorted [x] = True
sorted (x:xs) = x < head xs && sorted xs
-- head is safe because of the preceeding match.
次に、上記のヘルパーがtoList
提供するメソッドを使用できます。Data.Foldable
isBST :: Ord a => BST a -> Bool
isBST = sorted . toList
あなたが尋ねたように、これをより直接的に実装することもできます。データ型の誤った制約を削除したため、折り畳みの定義を簡素化できます。
cata :: (b -> a -> b -> b) -> b -> BST a -> b
cata _ z Void = z
cata f z (Node l x r) = f (cata f z l) x (cata f z r)
ここで、カタモルフィズムの結果をモデル化するためのデータ型が必要です。これは、ノードがない ( Z
)、厳密に増加するノードの範囲 ( )、またはT
失敗した ( X
)のいずれかです。
data T a = Z | T a a | X deriving Eq
そして、isBST
直接実装できます
isBST' :: Ord a => BST a -> Bool
isBST' b = cata phi Z b /= X where
phi X _ _ = X
phi _ _ X = X
phi Z a Z = T a a
phi Z a (T b c) = if a < b then T a c else X
phi (T a b) c Z = if b < c then T a c else X
phi (T a b) c (T d e) = if b < c && c < d then T a e else X
これは少し面倒なので、暫定的な状態を構成する方法を少し分解した方がよいでしょう:
cons :: Ord a => a -> T a -> T a
cons _ X = X
cons a Z = T a a
cons a (T b c) = if a < b then T a c else X
instance Ord a => Monoid (T a) where
mempty = Z
Z `mappend` a = a
a `mappend` Z = a
X `mappend` _ = X
_ `mappend` X = X
T a b `mappend` T c d = if b < c then T a d else X
isBST'' :: Ord a => BST a -> Bool
isBST'' b = cata phi Z b /= X where
phi l a r = l `mappend` cons a r
個人的には、おそらく Foldable インスタンスを使用するだけです。