I have this algebraic data type for representing binary trees:
data Tree a = Empty | Node a (Tree a) (Tree a)
deriving (Show, Eq)
Also, I have this simple type:
data Step = StepL | StepR
deriving (Show, Eq)
Also, I need to define a function that:
- takes a path of type
[Step], - a value to set.
The idea is that we start from the root, and follow the "directions" in
[Step]in order to get to the target node.
If the path does not "fall off" the path, we reset the target value to the desired value. (Note: We rebuild the entire tree.) If, however, the path fall off, we return the input tree intact.
My best attempt:
-- path -> value -> current node -> original root -> new node
setHelper :: [Step] -> a -> Tree a -> Tree a -> Tree a
setHelper [] value (Node _ l r) root = Node value l r
setHelper [StepL] value (Node _ Empty Empty) root = root
setHelper [StepR] value (Node _ Empty Empty) root = root
setHelper [StepL] value (Node nodeValue l Empty) root = setHelper [] value l root
setHelper [StepR] value (Node nodeValue Empty r) root = setHelper [] value r root
setHelper [StepL] value (Node nodeValue Empty _) root = root
setHelper [StepR] value (Node nodeValue _ Empty) root = root
setHelper [StepL] value (Node _ l r) root = setHelper [] value l root
setHelper [StepR] value (Node _ l r) root = setHelper [] value r root
setHelper (StepL:steps) value (Node _ l r) root = setHelper steps value l root
setHelper (StepR:steps) value (Node _ l r) root = setHelper steps value r root
set :: [Step] -> a -> Tree a -> Tree a
set steps value root = setHelper steps value root root
Now, when I:
print (set [] 1 (Node 0 Empty Empty))
print (set [StepL, StepL] 1 (Node 0 (Node 0 (Node 0 Empty Empty) (Node 0 Empty Empty)) (Node 0 Empty Empty)))
print (set [StepL, StepR] 1 (Node 0 Empty Empty))
I get:
Node 1 Empty Empty
Node 1 Empty Emtpy
*** Exception: Main.hs: ... Non-exhaustive patterns in function setHelper
As you can see, my implementation is both not correct and buggy.
Q: How to fix the both issues?
The problem is none of your clauses match
setHelper [StepR] 0 Empty root. If you enable the compiler warnings (for exampleghc -Wall ...) it will tell you so, too:You probably just want to convert these:
into the same form as the last 2 clauses ie
[StepX]→(StepX:steps)etc.There is also some redundant clauses for example
both match with
setHelper [StepL] 1 (Node 1 Empty Empty) Emptyand do the exact same thing.Now for the buggyness part: You've built yourself up quite a lot of (mostly redundant) clauses so let's step back and think about this problem. When we call
setwhat are the cases where we know we're finished (aka base cases), there is nothing left to do, I come up with the following two:We've walked off the path and landed on an
Emptyin this case we just return the same thing as we want to change nothing in the tree. We don't need to return
rootas anything before us can be (and should be) handled by a prior invocation ofset(i.e. the recursive call)We've reached the end of our journey
Here we just return a new node with the old subtrees as our children, no need to rebuild these as they'll stay the same. Again we don't care for anything that happened before us, the recursive cases will handle these.
Now that the base cases are properly handled we can look at the recursive steps:
l == Emptyour base case will handle that, but what we have to do is put the newly generated case of our recursive call in place of the oldl.rinstead oflso the implementation is left to the reader.And with those 4 clauses the
setfunction is total and produces the new tree.Note especially:
[StepL](a single step) as the combination of our recursive case(StepL:steps)with the[]base case will handle that for us.rootas we'll just always build up a new tree walking along the path.rootin every invocation we also don't need theset<->setHelperpair to simplify the invocation ofset[StepX]matches. Usually a recursive(x:xs)and a[]will do the job just fine in combination.