Een leuke manier van het coderen van dit is te leunen op de traversal door Data.Foldable.
{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Data.Foldable
import Data.Monoid
We kunnen een voorbeeld van deze automatisch met behulp van een verlenging af te leiden, maar we moeten de velden van de Node constructeur herschikken om ons te voorzien van een in-orde traversal.
Terwijl we toch bezig zijn, moeten we de beperkingen van het soort gegevens zelf te elimineren. Zij bieden in feite geen voordeel, en is uit de taal van Haskell 2011. verwijderd (Wanneer u dergelijke beperkingen te gebruiken moet je ze op gevallen van klassen, niet van het type data.)
data BST a
= Void
| Node
{ left :: BST a
, val :: a
, right :: BST a
} deriving (Eq, Ord, Read, Show, Foldable)
Eerst bepalen we wat het betekent voor een lijst strikt te sorteren.
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.
Dan kunnen we het gebruiken toListmethode die door Data.Foldableen de hierboven helper.
isBST :: Ord a => BST a -> Bool
isBST = sorted . toList
We kunnen dit ook uit te voeren meer direct, als je dat vraagt. Aangezien wij de valse beperkingen van het type data verwijderd, kunnen we de definitie van uw fold te vereenvoudigen.
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)
Nu moeten we een data type om het resultaat van onze catamorfisme, namelijk dat we ofwel hebben geen knooppunten (model Z), of een reeks van strikt stijgende knooppunten ( T) of hebben gefaald ( X)
data T a = Z | T a a | X deriving Eq
En we kunnen vervolgens in de praktijk isBSTdirect
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
Dit is een beetje vervelend, dus misschien zou het beter zijn om de manier waarop we samen de interim-staten een beetje ontleden:
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
Persoonlijk zou ik waarschijnlijk gewoon gebruik maken van de Opvouwbare instantie.