Reputation: 57
I have the following type:
data NestedList a = Elem a | List [NestedList a]
I'm trying to write a function that returns the most nested list within a given list, but I don't know where to start. Any help appreciated!
Example:
input of function is something like:
(List [List [List [List [Elem 1, Elem 2, Elem 3], Elem 5, Elem 6], List [Elem 5, Elem 6]], List [Elem 5, Elem 6]])
desired output of function:
(List [Elem 1, Elem 2, Elem 3])
Upvotes: 3
Views: 738
Reputation: 54058
I'll give an example using binary trees instead, which are very similar to your structure. You'll have the exercise of converting it to work with your data type.
Say I have a binary tree
data Tree a
= Leaf a
| Node (Tree a) (Tree a)
deriving (Eq, Show)
and I want to find the values that have the maximum depth (there can be more than one!). How I would solve this would be to traverse down each branch recursively, recording the depth as I go, and then return back the value(s) at the bottom along with their depth.
First, I'll define my function structure
import Data.List (sortBy, groupBy)
import Data.Ord (comparing)
import Data.Function (on)
getDeepest :: Tree a -> [a]
getDeepest tree
= map fst -- Strip the depth from the values
. head -- Get just the ones with the largest depth
. groupBy ((==) `on` snd) -- Group by the depth
. sortBy (flip (comparing snd)) -- Reverse sort by the depth (largest first)
$ go tree 0 -- Find all the "bottom" nodes
where
go :: Tree a -> Int -> [(a, Int)]
go (Leaf a) n = undefined
go (Node l r) n = undefined
This is a common recursion format you'll see in Haskell. I have a local helper function that carries an additional value that I want to initialize at a particular value, in this case the depth 0
. I've already included the logic that I know I want in order to get the output in a nice format. The flip (comparing snd)
will do a reverse sort, so the largest depth will come first. We then group by the depth, extract the first group, then strip the depth from the values.
Now we just have to define what go
does. We know that when we hit the bottom, we want to add the value to our accumulator with the depth that we found, so
go (Leaf a) n = [(a, n)]
That case is pretty easy, we just make a tuple from the value and the depth and wrap it as a list. For the other case, we want to traverse down each branch, find the deepest elements, and return the deepest from both branches
go (Node l r) n = go l (n + 1) ++ go r (n + 1)
This is where the recursion happens. While this is certainly not the most efficient algorithm (Haskell lists aren't great for this, but we'll use them for simplicity), it is pretty simple still. All we do is go down each side and increase our depth by 1
. So the whole algorithm together:
getDeepest :: Tree a -> [a]
getDeepest tree
= map fst -- Strip the depth from the values
. head -- Get just the ones with the largest depth
. groupBy ((==) `on` snd) -- Group by the depth
. sortBy (flip (comparing snd)) -- Reverse sort by the depth (largest first)
$ go tree 0 -- Find all the "bottom" nodes
where
go :: Tree a -> Int -> [(a, Int)]
go (Leaf a) n = [(a, n)]
go (Node l r) n = go l (n + 1) ++ go r (n + 1)
So as an example:
myTree :: Tree Int
myTree =
Node
(Node
(Leaf 1)
(Node
(Leaf 2)
(Leaf 3)))
(Leaf 4)
Which can be visualized as
Node
/ \
Node Leaf 4
/ \
Leaf 1 Node
/ \
Leaf 2 Leaf 3
Then by applying getDeepest
to it returns [2, 3]
. I encourage you to drop the type signature from getDeepest
and try deleting the various functions before go tree 0
(starting at the top) so that you can see what it looks like at each step, it should help you visualize the algorithm a bit better.
Upvotes: 2