Une belle façon de coder ceci est de se pencher sur le parcours fourni par Data.Foldable.
{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Data.Foldable
import Data.Monoid
On peut en déduire une instance de automatiquement à l'aide d'une extension, mais nous devons réorganiser les champs du constructeur de nœud pour nous fournir un ordre dans traversal.
Alors que nous sommes, nous devons éliminer les contraintes qui pèsent sur le type de données lui-même. Ils fournissent en fait aucun avantage, et a été retiré de la langue de Haskell 2011. (Si vous voulez utiliser ces contraintes que vous devez les mettre sur des instances de classes, et non sur le type de données.)
data BST a
= Void
| Node
{ left :: BST a
, val :: a
, right :: BST a
} deriving (Eq, Ord, Read, Show, Foldable)
D' abord , nous définissons ce que cela signifie pour une liste à strictement triée.
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.
Ensuite , nous pouvons utiliser la toListméthode fournie par Data.Foldableet l'assistant ci - dessus.
isBST :: Ord a => BST a -> Bool
isBST = sorted . toList
Nous pouvons également mettre en œuvre ce plus directement, comme vous avez demandé. Étant donné que nous avons supprimé les contraintes parasites sur le type de données, nous pouvons simplifier la définition de votre pli.
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)
Maintenant , nous avons besoin d' un type de données pour modéliser le résultat de notre catamorphisme, qui est que nous avons soit pas de nœuds ( Z), ou une gamme de nœuds strictement croissantes ( T) ou ont échoué ( X)
data T a = Z | T a a | X deriving Eq
Et nous pouvons mettre en œuvre isBSTdirectement
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
Ceci est un peu fastidieux, il serait peut-être préférable de décomposer la façon dont nous composons les états intermédiaires un peu:
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
Personnellement, je serais probablement juste utiliser l'instance Pliable.