I have this question to do:
“Define a function findpath:: BTree a -> a -> Path (where Btree a is defined as in the previous questions) that, given a binary tree t and a value x, returns a path from the root of t to a leaf with value x if there is one, and the value Nothing otherwise. The running time of your program should be linear in the number of nodes in the tree.”
So far I have:
data Maybe a = Nothing | Just a
data BTree a = Leaf a | Fork (BTree a) (BTree a)
type Path = Maybe [Dir]
data Dir = Left | Right
findpath :: Eq a => BTree a -> a -> Path
findpath (Leaf y) x = if y==x then ??? else Nothing
findpath (Fork l r) x = nodepath (findpath l x) (findpath r x) where
nodepath :: Path -> Path -> Path
nodepath Nothing Nothing = Nothing
nodepath Nothing pathr = Just [R]
nodepath pathl Nothing = Just [L]
I still can’t work how to build the final answer in the (Leaf y) case
Your language, about what you thought the program should do, suggests to me that you need help to escape from the trap of imperative thinking. Let me try to offer some help, based on thinking about what things are, not what things do.
For
findpath (Leaf y) x, you’re heading in the right direction. You just need to giveifa lowercasei, and think about what the correctPathto aLeafmust be.Now, let’s think about the other possibility. You know more than that it’s some
t. You know that you’re really trying to figure out whatis (what it
=, indeed), because that’s the other possibility for aBTree. Think of splitting the problem by asking “Is thisBTreea(Leaf y)or a(Node l r)?” as one conceptual step of program design. Now, in order to figure out what the above left-hand side equals, you’re entitled to some recursively computed information, namely whatand
are. If you know
Pathinformation for bothlandr, can you say what thePathfor the wholeNode l ris? Let me rephrase that question by writing it in Haskell:I have expressed my question by introducing a helper function
nodepathwhich takes as arguments the recursively computed information. Now you can try to implementnodepathby pattern matching on those two paths for the left and right subtrees, respectively. If you know whether they are(Just p)orNothing, then you should be able to say what the path for the whole node must be.Lesson one, the useful thoughts are of the form: “If this is like such-and-such, then that must be so-and-so.”. Being, not doing.
Lesson two, the basic method of programming over a datatype is: split into constructor cases (
LeafversusNode,JustversusNothing); collect useful information from any substructures by recursive calls; say what the value for the whole structure must be.If you follow my advice and figure out what
nodepathshould be, you may find that it’s simple enough not to merit being a separate named definition. In that case, just replace thenodepathcall with its meaning and cut out thewhere-clause. But it’s good to start by introducingnodepath, as it expresses a useful conceptual step towards solving the problem.