Una buena manera de codificar esto es para apoyarse en el recorrido proporcionada por Data.Foldable.
{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Data.Foldable
import Data.Monoid
Podemos obtener una instancia del mismo de forma automática utilizando una extensión, pero tenemos que cambiar el orden de los campos del nodo constructor para proporcionar un recorrido en orden.
Mientras estamos en ello, debemos eliminar las limitaciones en el tipo de datos en sí. Que en realidad no proporcionan ningún beneficio, y se ha quitado de la lengua como de Haskell 2011. (Cuando se desea utilizar este tipo de limitaciones que debe ponerlos en instancias de clases, no en el tipo de datos.)
data BST a
= Void
| Node
{ left :: BST a
, val :: a
, right :: BST a
} deriving (Eq, Ord, Read, Show, Foldable)
Primera definimos lo que significa que una lista esté estrictamente ordenada.
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.
entonces podemos utilizar el método toList
proporcionada por Data.Foldable
y el ayudante anteriormente.
isBST :: Ord a => BST a -> Bool
isBST = sorted . toList
También podemos implementar esto más directamente, como usted pidió. Como eliminamos las restricciones espurias en el tipo de datos, podemos simplificar la definición de su doblez.
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)
Ahora necesitamos un tipo de datos para modelar el resultado de nuestra catamorphism, que es que o bien no tienen nodos (Z
), o un rango de estrictamente creciente nodos (T
) o han fallado (X
)
data T a = Z | T a a | X deriving Eq
Y entonces podemos aplicar directamente 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
Esto es un poco tedioso, por lo perha ps sería mejor para descomponer la forma en que componemos los estados intermedios un poco:
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
En lo personal, yo probablemente sólo tiene que utilizar la instancia plegable.
¿Cómo debo manejar la carcasa cuando doblan se aplica a 'Void'? Quiero decir, el valor mínimo y máximo ... – gremo
El valor inicial 'z' también debería cambiarse para que no sea booleano. Hazlo regresar (Verdadero, (+ inf, -inf)) o algo así. De todos modos, las otras soluciones que implican convertir el árbol en una lista parecen ser más parecidas a las que realmente necesita. – hugomg