Checking if an element exists in a tree - haskell

I defined a tree as follows and want to check if a given element is an element of a given tree or not. Here's the code I have, but it doesn't work fully. It only seems to work on the head.
I'm new to Haskell and can't figure out how to fix this.
data MyTree a = Leaf a | Node [MyTree a] deriving (Eq)
isElem :: (Eq a) => MyTree a -> MyTree a -> Bool
isElem (Node []) _ = error
isElem (Node (h:t)) (Leaf a)
| (Leaf a) `elem` (h:t) = True
| otherwise = False
Here's my second attempt.
isElem :: (Eq a) => MyTree a -> MyTree a -> Bool
isElem (Node []) a = False
isElem (Node (h:t)) a
| (a == h) = True
| otherwise = (isElem (Node t) a)

I assume you're interested in implementing the function yourself (and bheklir addressed that), but you could also just derive it automatically:
{-# LANGUAGE DeriveFoldable #-}
import qualified Data.Foldable as F
data MyTree a = Leaf a | Node [MyTree a] deriving (Eq, F.Foldable)
isElem = F.elem

If we had a binary tree
data BinTree a
= Empty
| Branch a (BinTree a) (BinTree a)
deriving (Eq, Show)
Then we could do a search as
isElem :: (Eq a) => a -> BinTree a -> Bool
isElem a Empty = False
isElem a (Branch c left right)
| a == c = True
| otherwise = isElem a left || isElem a right
Notice how in the second guard, I recursively call isElem on both the left and the right leaves. If one of them returns True, then the overall return value will be True because of the logical or (||). This recursion is the key to the algorithm.
I'd like you to think about how to apply recursion to not just 2 branches, but N branches, then combine them using or. Additionally, I would recommend that you define the function as
isElem :: (Eq a) => a -> MyTree a -> Bool
And to avoid the use of error. This function should never error. Either an element is in the tree or it isn't, there isn't a third state it could be in. Just return False instead.

In this answer, I explain how to implement isElem from scratch by following the structure of the datatype MyTree.
Following the structure
Your isElem function should look for an element all over a MyTree, so probably the structure of isElem should be similar to the structure of MyTree.
data MyTree a = Leaf a | Node [MyTree a]
To make the structure of MyTree clearer, let's call [MyTree a] a forest, because a forest contains multiple trees.
data MyTree a = Leaf a | Node (MyForest a)
type MyForest a = [MyTree a]
Now we have two types, and this helps us understand that we need two functions:
isTreeElem :: MyTree a -> a -> Bool
isForestElem :: MyForest a -> a -> Bool
I changed the second argument from MyTree a to a, as proposed in bheklilr's answer.
Implementing isTreeElem
Let's start with isTreeElem. We follow the structure of MyTree by pattern matching:
isTreeElem (Leaf x) y = ...
isTreeElem (Node forest) y = ...
In the equation for Leaf, there is only one location in the tree where the y could be. So we return True if x == y and False if x /= y. In other words:
isTreeElem (Leaf x) y = x == y
In the equation Node, we need to keep searching for y in the whole forest. So we simply call isForestElem.
isTreeElem (Node forest) y = isForestElem forest y
This is the first place where our decision to introduce MyForest and isForestElem pays out: We can follow the structure of trees without thinking about forests. In this case, we see that a node contains a forest, so we immediately know that isTreeElem (Node ...) should call isForestElem. To implement this, we don't have to think about what forests are or how isForestElem works, because these decisions are encapsulated in the isForestElem function.
Implementing isForestElem
At some point, we also need to implement isForestElem. A forest is just a list, so we follow the structure of [...] by pattern matching:
isForestElem [] y = ...
isForestElem (tree : forest) y = ...
In the case for [], there is no place left to search for y, so we return False:
isForestElem [] y = False
In the case for tree : forest, the y could either be in the tree or in the forest, so we call the corresponding functions and combine their result with the or operator ||:
isForestElem (tree : forest) y = isTreeElem tree y || isForestElem forest y
The is the second place where our decision to introduce MyForest and isForestElem pays out: We can follow the structure of forests without thinking about trees. In this case, we see that a forest contains a tree, so we immediately know that isForestElem (... : ...) should call isTreeElem. To implement this, we don't have to think about what trees are or how isTreeElem works, because these decisions are encapsulated in the isTreeElem function.
Note that we could have implemented isForestElem first and isTreeElem second without changing any of the reasoning.
The code
Here's what we have so far:
data MyTree a = Leaf a | Node (MyForest a)
type MyForest a = [MyTree a]
isTreeElem :: MyTree a -> a -> Bool
isTreeElem (Leaf x) y = x == y
isTreeElem (Node forest) y = isForestElem forest y
isForestElem :: MyForest a -> a -> Bool
isForestElem [] y = False
isForestElem (tree : forest) y = isTreeElem tree y || isForestElem forest y
Using list functions
If you want to write more functions that operate on MyTree, you can follow this recipe of always implementing a forest and tree version of every function. But at some point, you might notice that the forest versions are all very similar and kind-of boring. They feel like boilerplate that doesn't contribute a lot to your programs, but needs to be written anyway. If and when that's the case, you might want to use the list operations from the Prelude and Data.List modules instead of writing your own forest operations. For example:
isForestElem forest x = any (\tree -> isTreeElem tree x) forest
Or if we change the argument order of isTreeElem and isForestElem, we can even write:
isTreeElem :: a -> Tree a -> Bool
isTreeElem = ...
isForestElem :: a -> Forest a -> Bool
isForestElem x = any (isTreeElem x)
Now isForestElem is so simple that we might want to inline it into isTreeElem:
isTreeElem :: a -> MyTree a -> Bool
isTreeElem y (Leaf x) = x == y
isTreeElem y (Node forest) = any (isTreeElem y) forest
At some point, you should start to write code that is more like the last version of isTreeElem. But as an intermediate step, while you're learning Haskell (and functional programming?), you might want to focus on code like the firest version above, with separate isTreeElem and isForestElem functions. If you carefully structure your data types, the simple principle that the structure of the code should follow the structure of the data can get you a long way.
PS. By the way, the deriving Foldable trick in András Kovács's answer is also following the structure of the datatype, but automates it more.

Related

How does repmin place values in the tree in Haskell?

I really like the repmin problem:
Write down repmin :: Tree Int -> Tree Int, which replaces all the numbers in the tree by their minimum in a single pass.
If I were writing something like this in python, I would go for passing values by their reference (let's say one-element lists instead of numbers is good enough):
def repmin(tree, wrapped_min_link=None):
x, subforest = tree
if wrapped_min_link is None:
wrapped_min_link = [x]
else:
[m] = wrapped_min_link
wrapped_min_link = [min(m, x)]
n = len(subforest)
subforest_min = [None] * n
for i in range(n):
if subforest[i]:
subforest_min[i] = repmin(subforest[i], wrapped_min_link)
return (wrapped_min_link, subforest_min)
It seems to me like a fitting way to wrap one's head around the knot-tying solution in Haskell (I wrote this one for rose trees from Data.Tree):
copyRose :: Tree Int -> Int -> (Tree Int, Int)
copyRose (Node x []) m = (Node m [], x)
copyRose (Node x fo) m =
let
unzipIdMinimum =
foldr (\ ~(a, b) ~(as, bmin) -> (a:as, b `min` bmin)) ([], maxBound :: Int)
(fo', y) = unzipIdMinimum . map (flip copyRose m) $ fo
in (Node m fo', x `min` y)
repmin :: Tree Int -> Tree Int
repmin = (loop . uncurry) copyRose
Yet, I reckon the solutions to work very differently. Here is my understanding of the latter one:
Let us rewrite loop for (->) a bit:
loop f b = let cd = f (b, snd cd) in fst cd
I reckon it to be loop for (->)'s workalike as snd gives the same degree of laziness as pattern-matching within let.
So, when repmin traverses through the tree, it is:
Building up the minimum in the tree to be returned as the second element of the pair.
Leaves snd $ copyRose (tree, m) behind in every node.
Thus, when the traversal comes to an end, the programme knows the value of snd $ copyRose (tree, m) (that is, the minimum in the tree) and is able to show it whenever some node of the tree is being computed.
Do I understand repmin in Haskell correctly?
This is more an extended comment than an answer, but I don't really think of your implementation as single-pass. It looks like it traverses the tree once, producing a new, lazily-generated, tree and the global minimum, but it actually produces a lazily generated tree and an enormous tree of thunks that will eventually calculate the minimum. To avoid this, you can get closer to the Python code by generating the tree eagerly, keeping track of the minimum as you go.
You'll note that I've generalized the type from Int to an arbitrary Ord type. You'll also note that I've used to different type variables to refer to the type of elements in the given tree and the type of the minimum passed in to generate a new tree—this lets the type system tell me if I mix them up.
repmin :: Tree a -> Tree a
repmin = (loop . uncurry) copyRose
copyRose :: Ord a => Tree a -> b -> (Tree b, a)
copyRose (Node x ts) final_min
| (ts', m) <- copyForest x ts final_min
= (Node final_min ts', m)
copyForest :: Ord a => a -> [Tree a] -> b -> ([Tree b], a)
copyForest !m [] _final_min = ([], m)
copyForest !m (t : ts) final_min
| (t', m') <- copyTree m t final_min
, (ts', m'') <- copyForest m' ts final_min
= (t' : ts', m'')
copyTree :: Ord a => a -> Tree a -> b -> (Tree b, a)
copyTree !m (Node x ts) final_min
| (ts', m') <- copyForest (min m x) ts final_min
= (Node final_min ts', m')
Exercise: rewrite this in monadic style using ReaderT to pass the global minimum and State to keep track of the minimum so far.

Haskell Function for checking if element is in Tree, returning Depth

I am currently doing an assigment for a class in which I have to implement a function which checks if an element is in a tree.
It is supposed to return Nothing when the element is not in the tree and Just (depth at which it was found) when it is.
An example:
sample1
##1
#3 2
###7 5 6 4
- contains 6 sample1 returns Just 2
- contains 1 sample1 returns Just 0
- contains 2 sample1 returns Just 1
- contains 8 sample1 returns Nothing
Here is what we are given:
Heap functional data structure:
module Fdata.Heap where
-- A signature for min-heaps
data Heap e t = Heap {
empty :: t e,
insert :: e -> t e -> t e,
findMin :: t e -> Maybe e,
deleteMin :: t e -> Maybe (t e),
merge :: t e -> t e -> t e,
contains :: e -> t e -> Maybe Int
}
An implementation of self-adjusting heaps:
import Fdata.Heap
import Fdata.Tree
-- An implementation of self-adjusting heaps
heap :: (Eq e, Ord e) => Heap e Tree
heap = Heap {
empty = Empty,
insert = \x t -> merge' (Node x Empty Empty) t,
findMin = \t -> case t of
Empty -> Nothing
(Node x _ _) -> Just x,
deleteMin = \t -> case t of
Empty -> Nothing
(Node _ l r) -> Just (merge' r l),
merge = \l r -> case (l, r) of
(Empty, t) -> t
(t, Empty) -> t
(t1#(Node x1 l1 r1), t2#(Node x2 l2 r2)) ->
if x1 <= x2
then Node x1 (merge' t2 r1) l1
else Node x2 (merge' t1 r2) l2,
contains = \x t -> case (x,t) of
(x,Empty)-> Nothing
(x,tx#(Node x1 l1 r1) ->
|x==x1 = Just 0
|x>x1 = (1+ (contains x l)
|x<x1 = (1+ (contains x r)
}
where
merge' = merge heap
The tree implementation
module Fdata.Tree where
import Fdata.Heap
data Tree x
= Empty
| Node x (Tree x) (Tree x)
deriving (Eq, Show)
leaf x = Node x Empty Empty
-- Convert a list to a heap
list2heap :: Heap x t -> [x] -> t x
list2heap i = foldl f z
where
f = flip $ insert i
z = empty i
-- Convert a heap to a list
heap2list :: Heap x t -> t x -> [x]
heap2list i t
= case (findMin i t, deleteMin i t) of
(Nothing, Nothing) -> []
(Just x, Just t') -> x : heap2list i t'
I am supposed to implement the contains function in the implementation for self-adjusting heaps.
I am not allowed to use any helper functions and I am supposed to use the maybe function.
My current implementation:
contains = \x t -> case (x,t) of
(x,Empty) -> Nothing
(x,tx#(Node x1 l1 r1))
|x==x1 -> Just 0
|x>x1 -> (1+ (contains x l1)
|x<x1 -> (1+ (contains x r1)
This does not work, since I get a parse error on input |.
I really dont know how to fix this since I did use 4 spaces instead of tabs and according to this: https://wiki.haskell.org/Case
the syntax is correct...
I once managed to fix this, but I got a type error about (1+ (contains x l), so this probably is not correct.
Any hint would be appreciated.
EDIT:
Thanks to everyone who answered!
Really appreciate that everyone took the time to explain their answers in great detail.
First of all:
there were some smaller mistakes, as pointed out by some of you in the comments:
I missed one closing parenthesis and accidentially named one argument l1 and another r1 and afterwards used r and l.
Fixed both mistakes.
Someone wrote that I do not need to use a lambda function. The problem is when I use something like:
contains _ Empty = Nothing
I get the error:
parse Error on input '_'.
However, lambda functions do not give me any errors about the input arguments.
Currently the only function that works without any errors is:
contains = \e t -> case (e,t) of
(_,Empty) -> Nothing
(e , Node x t1 t2) ->
if e == (head (heap2list heap (Node x t1 t2)))
then Just 0
else if (fmap (+1) (contains heap e t1))== Nothing
then (fmap (+1) (contains heap e t2))
else (fmap (+1) (contains heap e t1))
Found at:
Counting/Getting "Level" of a hierarchical data
Found by:Krom
One way of structuring contains :: Eq a => a -> Tree a -> Maybe Integer is to first label each element in your tree with its depth, using something like this, then fold the tree to find the element you're looking for, pulling its depth out with it. You can do this without very much code!
Jumping right in where this answer left off, here's contains.
contains :: Eq a => a -> Tree a -> Maybe Integer
contains x = fmap fst . find ((== x) . snd) . labelDepths
That's the whole function! This is classic functional programming style: rather than hand-crank a bespoke recursive tree traversal function I've structured the code as a pipeline of reusable operations. In Haskell pipelines are constructed using the composition operator (.) and are read from left to right. The result of labelDepths is passed to find ((== x) . snd), whose result is then passed to fmap fst.
labelDepths :: Tree a -> Tree (Integer, a), which I've explained in detail in the answer I linked above, attaches an Integer depth to each element of the input tree.
find :: Foldable t => (a -> Bool) -> t a -> Maybe a is a standard function which extracts the first element of a container (like a tree, or a list) that satisfies a predicate. In this instance, the Foldable structure in question is a Tree, so t ~ Tree and find :: (a -> Bool) -> Tree a -> Maybe a. The predicate I've given to find is ((== x) . snd), which returns True if the second element of its input tuple equals x: find ((== x) . snd) :: Tree (Integer, a) -> Maybe (Integer, a). find works by folding the input structure - testing its elements one at a time until it finds one that matches the predicate. The order in which elements are processed is defined by the container's Foldable instance, of which more below.
fmap :: Functor f => (a -> b) -> f a -> f b is another standard function. It applies a mapping function uniformly to each element of a container, transforming its elements from type a to type b. This time the container in question is the return value of find, which is a Maybe, so fmap :: (a -> b) -> Maybe a -> Maybe b. The mapping function I've supplied is fst, which extracts the first element of a tuple: fmap fst :: Maybe (Integer, a) -> Maybe Integer.
So putting it all together, you can see that this is a fairly direct implementation of my English description of the process above. First we label every element in the tree with its depth, then we find an element which matches the item we're looking for, then we extract the depth with which the element was previously labelled.
I mentioned above that Tree is a Foldable container. In fact, this isn't the case quite yet - there's no instance of Foldable for Tree. The easiest way to get a Foldable instance for Tree is to turn on the DeriveFoldable GHC extension and utter the magic words deriving Foldable.
{-# LANGUAGE DeriveFoldable #-}
data Tree x = Empty | Node x (Tree x) (Tree x) deriving Foldable
This automatically-implemented instance of Foldable will perform a preorder traversal, processing the tree in a top-down fashion. (x is considered to be "to the left of" l and r in the expression Node x l r.) You can adjust the derived traversal order by adjusting the layout of the Node constructor.
That said, I'm guessing that this is an assignment and you're not allowed to modify the definition of Tree or apply any language extensions. So you'll need to hand-write your own instance of Foldable, following the template at the bottom of this post. Here's an implementation of foldr which performs a preorder traversal.
instance Foldable Tree where
foldr f z Empty = z
foldr f z (Node x l r) = f x (foldr f (foldr f z r) l)
The Node case is the interesting one. We fold the tree from right to left (since this is a foldr) and from bottom to top. First we fold the right subtree, placing z at the rightmost leaf. Then we use the aggregated result of the right subtree as the seed for folding the left subtree. Finally we use the result of folding all of the Node's children as the aggregator to apply to f x.
Hopefully you didn't find this answer too advanced! (Happy to answer any questions you have.) While the other answers do a good job of showcasing how to write recursive tree traversal functions, I really wanted to give you a glimpse of the real power of functional programming. When you think at a higher level - breaking down a problem into its component parts, structuring operations as pipelines, and learning to spot common patterns like zipping, folding and mapping - you can be very productive and solve problems with very little code.
An instance of Foldable for a binary tree
To instantiate Foldable you need to provide a definition for at least foldMap or foldr.
data Tree a = Leaf
| Node (Tree a) a (Tree a)
instance Foldable Tree where
foldMap f Leaf = mempty
foldMap f (Node l x r) = foldMap f l `mappend` f x `mappend` foldMap f r
foldr f acc Leaf = acc
foldr f acc (Node l x r) = foldr f (f x (foldr f acc r)) l
This implementation performs an in-order traversal of the tree.
ghci> let myTree = Node (Node Leaf 'a' Leaf) 'b' (Node Leaf 'c' Leaf)
-- +--'b'--+
-- | |
-- +-'a'-+ +-'c'-+
-- | | | |
-- * * * *
ghci> toList myTree
"abc"
The DeriveFoldable extension allows GHC to generate Foldable instances based on the structure of the type. We can vary the order of the machine-written traversal by adjusting the layout of the Node constructor.
data Inorder a = ILeaf
| INode (Inorder a) a (Inorder a) -- as before
deriving Foldable
data Preorder a = PrLeaf
| PrNode a (Preorder a) (Preorder a)
deriving Foldable
data Postorder a = PoLeaf
| PoNode (Postorder a) (Postorder a) a
deriving Foldable
-- injections from the earlier Tree type
inorder :: Tree a -> Inorder a
inorder Leaf = ILeaf
inorder (Node l x r) = INode (inorder l) x (inorder r)
preorder :: Tree a -> Preorder a
preorder Leaf = PrLeaf
preorder (Node l x r) = PrNode x (preorder l) (preorder r)
postorder :: Tree a -> Postorder a
postorder Leaf = PoLeaf
postorder (Node l x r) = PoNode (postorder l) (postorder r) x
ghci> toList (inorder myTree)
"abc"
ghci> toList (preorder myTree)
"bac"
ghci> toList (postorder myTree)
"acb"
This function doesn't need to be a lambda:
contains x t =
Adding x to the case serves no purpose, since you only match it back to x. You can instead use pattern matching in the function head:
contains _ Empty = Nothing
The Node case has three sub-cases, where the value being searched for is less-than, greater-than, or equal to the value in the Node. If you order them that way, you get a symmetry from the less-than and greater-than tests, and can handle the equal case with an otherwise.
When recusring, you are going to get a Maybe Int, to which you want to add one. You can't do that directly because the Int is inside the Maybe. Normally, you would lift the addition, but I suspect that this is where the required call to maybe should go (however unnatural it may seem):
contains x (Node x' l r) | x < x' = maybe Nothing (Just . (+1)) $ contains x l
| x > x' = maybe Nothing (Just . (+1)) $ contains x r
| otherwise = Just 0
Instead of using maybe, the (+1) could have been lifted into the Maybe with fmap (or <$>):
... = fmap (+1) $ contains ...
Using maybe is unnatural because it has to explicitly pass the Nothing, and also re-wrap the Just.
This does not work, since I get a parse error on input |
Your previous line misses a closing parenthesis.
I got a Typ error about (1+ (contains x l)), so this probably is not correct.
The idea is totally correct, the issue is that contains x l returns a Maybe Int instead of an Int so you cannot directly add to that. You can only add to the result when it's a Just. There's a helper function that does exactly that, do something to Justs and keep Nothings: fmap (from Functor).
contains = \x t -> case (x,t) of
(x,Empty)-> Nothing
(x,tx#(Node x1 l1 r1))
|x==x1 -> Just 0
|x>x1 -> fmap (1+) (contains x l)
|x<x1 -> fmap (1+) (contains x r)
Btw, I'd write this as
contains x Empty = Nothing
contains x (Node v l r) = if x == v
then Just 0
else fmap (+1) $ contains x $ if x > v then l else r

zipWith for trees in Haskell

I am learning Haskell using The Haskell School of Expression: Learning Functional Programming through Multimedia and I am unsure how to go about solving this exercise.
Using the definition of trees given by
data Tree a = Node (Tree a) (Tree a) | Leaf a
Define tree versions of the list functions zip and zipWith. There
will be cases at the leaves or where trees are of different shapes
where you’ll have to make design decisions. Try to make your decisions
as elegant as possible.
For zip I have this but I am unsure if it is "elegant"
zipTree :: Tree a -> Tree b -> Tree (a,b)
zipTree (Leaf a) (Leaf b) = Leaf (a,b)
zipTree (Node l1 r1) (Node l2 r2) =
let l = zipTree l1 l2
r = zipTree r1 r2
in Node l r
-- Problems...
zipTree (Node _ _) (Leaf _) = Node undefined undefined
zipTree (Leaf _) (Node _ _) = Node undefined undefined
And I am unsure how to adapt it to have zipWith functionality although I do know a elegant definition of zipWith.
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
zipWith _ _ _ = []
First of all I believe your solution is not elegant because you are using undefined.
You want to avoid partial functions, and structures, as much as possible and simply inserting some Node undefined undefined in a tree structure doesn't sound like a good idea.
Think about this: once you have a Node undefined undefined how would it act with the other operations on the tree? You'd probably get tons of exceptions.
So you have to find an alternative.
zipTree (Node l r) (Leaf a) = Node x y
where
x = ... ?
y = ... ?
Now how should x and y be defined? x should depend on l and Leaf a while y should depend on r and Leaf a.
The easiest way to define this case is to simply perform the recursive calls:
zipTree (Node l r) a#(Leaf _) = Node (zipTree l a) (zipTree r a)
So we are zipping all leafs in the l and r subtrees with the leaf a.
As for the zipWithTree: that's easy. You have to add an f parameter and use f x y instead of (x, y) when you perform the zipping, which is done only in the Leaf v Leaf case:
zipWithTree f (Leaf a) (Leaf b) = Leaf $ f a b
Obviously you have to add the f parameter to all the rules and pass f to recursive calls.
Note that there is also an other way to define zipWith, which is:
zipTree (Node _ _) (Leaf a) = Leaf (undefined, a)
This still isn't a good solution because it introduces an undefined however it has some advantages against the Node undefined undefined solution:
It acts for similarly to zip from lists. zip [1,2] [1,2,3,4] == [(1,1), (2,2)] so you stop when the shorter list ends.
The undefined is inside the value. This allows for example to have:
mapTree snd (zipTree x y) == y
whenever x has longer branches. Using Node undefined undefined you have mapTree f (zipTree x y) is always an exception whenever the trees aren't isomorphic (because mapTree will try to evaluate undefined).

Haskell: Defining Trees using zip/zipWith [duplicate]

I am learning Haskell using The Haskell School of Expression: Learning Functional Programming through Multimedia and I am unsure how to go about solving this exercise.
Using the definition of trees given by
data Tree a = Node (Tree a) (Tree a) | Leaf a
Define tree versions of the list functions zip and zipWith. There
will be cases at the leaves or where trees are of different shapes
where you’ll have to make design decisions. Try to make your decisions
as elegant as possible.
For zip I have this but I am unsure if it is "elegant"
zipTree :: Tree a -> Tree b -> Tree (a,b)
zipTree (Leaf a) (Leaf b) = Leaf (a,b)
zipTree (Node l1 r1) (Node l2 r2) =
let l = zipTree l1 l2
r = zipTree r1 r2
in Node l r
-- Problems...
zipTree (Node _ _) (Leaf _) = Node undefined undefined
zipTree (Leaf _) (Node _ _) = Node undefined undefined
And I am unsure how to adapt it to have zipWith functionality although I do know a elegant definition of zipWith.
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
zipWith _ _ _ = []
First of all I believe your solution is not elegant because you are using undefined.
You want to avoid partial functions, and structures, as much as possible and simply inserting some Node undefined undefined in a tree structure doesn't sound like a good idea.
Think about this: once you have a Node undefined undefined how would it act with the other operations on the tree? You'd probably get tons of exceptions.
So you have to find an alternative.
zipTree (Node l r) (Leaf a) = Node x y
where
x = ... ?
y = ... ?
Now how should x and y be defined? x should depend on l and Leaf a while y should depend on r and Leaf a.
The easiest way to define this case is to simply perform the recursive calls:
zipTree (Node l r) a#(Leaf _) = Node (zipTree l a) (zipTree r a)
So we are zipping all leafs in the l and r subtrees with the leaf a.
As for the zipWithTree: that's easy. You have to add an f parameter and use f x y instead of (x, y) when you perform the zipping, which is done only in the Leaf v Leaf case:
zipWithTree f (Leaf a) (Leaf b) = Leaf $ f a b
Obviously you have to add the f parameter to all the rules and pass f to recursive calls.
Note that there is also an other way to define zipWith, which is:
zipTree (Node _ _) (Leaf a) = Leaf (undefined, a)
This still isn't a good solution because it introduces an undefined however it has some advantages against the Node undefined undefined solution:
It acts for similarly to zip from lists. zip [1,2] [1,2,3,4] == [(1,1), (2,2)] so you stop when the shorter list ends.
The undefined is inside the value. This allows for example to have:
mapTree snd (zipTree x y) == y
whenever x has longer branches. Using Node undefined undefined you have mapTree f (zipTree x y) is always an exception whenever the trees aren't isomorphic (because mapTree will try to evaluate undefined).

Trying to implement path record for Haskell binary tree search

I've been playing around with binary trees in Haskell, and I am attempting to implement a dfs variant that returns the path (composed of left and rights) from the root node to the node containing the value searched for. I think it would be best to return a Maybe Directions type.
Here is what has been implemented so far.
data Tree a = Empty | Node a (Tree a) (Tree a)
deriving (Show, Eq)
data Direction = L | R
deriving (Show)
type Directions = [Direction]
inTree :: (Eq a) => a -> Tree a -> [Direction]
inTree val (Node x l r)
| val == x = []
| l /= Empty = L:(inTree val l)
| r /= Empty = R:(inTree val r)
| otherwise =
but I have no idea how to have it traverse the entire tree. I feel as though I might be thinking too imperatively.
Your idea to use Maybe Direction is good. I would rewrite your function as follows:
inTree :: (Eq a) => a -> Tree a -> Maybe [Direction]
inTree val Empty = Nothing
inTree val (Node x l r)
| val == x = Just []
| otherwise = (fmap (L:) (inTree val l)) <|> (fmap (R:) (inTree val r))
fmaping a function f on a Maybe results in Nothing if the original value is Nothing and Just (f v) if it's Just v. In our case if the recursive call found the value (so it's returning a path Just [Direction]) we append the Direction we took at the current node.
The <|> operator comes from the Alternative instance of Maybe. It's a left biased choice on maybes. Here we use it to pick the subtree which returned Just [Direction] (if there was any).
A good exercise is to modify the code so that it returns all the paths to the xs in the tree.
Here is a less streamlined version similar to the style presented in the question. This might be useful for anyone on a more basic level of learning Haskell and not yet understanding the contents of the Control.Applicative library.
inTree :: Eq a => a -> Tree a -> Maybe [Direction]
inTree _ Empty = Nothing
inTree val (Node x l r)
| val == x = Just []
| otherwise = case inTree val l of Just ys -> Just (L : ys)
Nothing -> case inTree val r of Just ys -> Just (R : ys)
Nothing -> Nothing

Resources