I am trying to write a function that returns the min value in a tree.
The main problem is that I don't think that it really returns the min value and not just one random value of Leaf. Somebody said to me to use
minBound
, but I didn't find information for this function, so I tried to write it myself. Would someone say me where the problem in my code is, and maybe how to use minBound
instead of my function?
data MultTree a = Nil | Leaf a | Node a a (MultTree a) (MultTree a) deriving Show
minTreeValue :: MultTree a -> a
minTreeValue (Leaf a) = a
minTreeValue (Node a b Nil _) = a
minTreeValue (Node a b left _) = minTreeValue left
minBound
is a name defined by the Bounded
typeclass, to provide a common way of referring to the smallest element of a type. For example,
data Size = Small | Medium | Large deriving (Eq, Enum, Bounded)
Then minBound :: Size == Small
and maxBound :: Size == Large
.
It doesn't serve any use here. Even if minTreeValue
required a
to have a Bounded
instance, the smallest value in a given tree x
has little relation to the smallest value the tree can have, aside from the obvious minTreeValue x >= minBound
.
Your definition is optimized for a tree which you know has the binary search property, which says that given a value Node a b left right
, then
minTreeValue left <= a <= b <= minTreeValue right
In general, the tree might not have this property, in which case you always have to explicitly compare two values to get the smaller one. This requires a further
constraint on the type: a
must have an Ord
instance.
minTreeValue (Leaf a) = a
minTreeValue (Node a b left right) = minimum [a, b, minTreeValue left, minTreeValue right]
But, now we have a problem: minTreeValue Nil
isn't defined. But there also isn't
a valid default to use in the recursion. You might think you could use minBound :: a
, but that would be incorrect, because minBound
is not the smallest value in any empty tree, as there are no values in an empty tree. As a result, you are forced to avoid calling minTreeValue
on the empty tree at all.
minTreeValue :: Ord a => MultTree a -> a
minTreeValue (Leaf a) = a
minTreeValue (Node a b Nil Nil) = minimum [a, b]
minTreeValue (Node a b left Nil) = minimum [a, b, minTreeValue left]
minTreeValue (Node a b Nil right) = minimum [a, b, minTreeValue right]
minTreeValue (Node a b left right) = minimum [a, b, minTreeValue left, minTreeValue Right]
This is correct, but partial, as minTreeValue Nil
is still undefined. You have to avoid calling it on Nil
, even if such recursive calls have been avoided.
One solution is to change the type of minTreeValue
to Ord a => MultTree a -> Maybe a
, so that we can use Nothing
for the smallest value of an empty tree. This also lets us easily ignore recursive calls that return Nothing
. The catMaybes
function lets us discard Nothing
values from a list, then extract the values
from the remaining Just
values.
import Data.Maybe
minTreeValue :: Ord a => MultTree a -> Maybe a
minTreeValue Nil = Nothing
minTreeValue (Leaf a) = Just a
minTreeValue (Node a b left right) = Just (minimum candidates)
where candidates = a : b : catMaybes [minTreeValue left, minTreeValue right]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With