diff --git a/chapters/tree_traversal/code/haskell/TreeTraversal.hs b/chapters/tree_traversal/code/haskell/TreeTraversal.hs
new file mode 100644
index 000000000..8c28ed807
--- /dev/null
+++ b/chapters/tree_traversal/code/haskell/TreeTraversal.hs
@@ -0,0 +1,43 @@
+data Tree a = Node { node :: a
+ , forest :: [Tree a]
+ } deriving (Show)
+
+dfs :: Tree a -> [a]
+dfs (Node x ts) = x : concatMap dfs ts
+
+dfsPostOrder :: Tree a -> [a]
+dfsPostOrder (Node x ts) = concatMap dfsPostOrder ts ++ [x]
+
+dfsInOrder :: Tree a -> [a] -- For binary trees only
+dfsInOrder (Node x []) = [x]
+dfsInOrder (Node x [l]) = dfsInOrder l ++ [x] -- Single branch assumed to be left
+dfsInOrder (Node x [l, r]) = dfsInOrder l ++ [x] ++ dfsInOrder r
+dfsInOrder _ = error "Not a binary tree"
+
+bfs :: Tree a -> [a]
+bfs (Node x ts) = x : go ts
+ where go [] = []
+ go ts = map node ts ++ go (concatMap forest ts)
+
+toBin :: Tree a -> Tree a
+toBin (Node x ts) = Node x (map toBin $ take 2 ts)
+
+main = do
+ print $ dfs testTree
+ print $ dfsPostOrder testTree
+ print $ dfsInOrder $ toBin testTree
+ print $ bfs testTree
+
+testTree :: Tree Int
+testTree = Node 1 [ Node 2 [ Node 3 []
+ , Node 4 [ Node 5 []]
+ ]
+ , Node 6 [ Node 7 []
+ , Node 8 [ Node 9 [ Node 10 [ Node 11 []]
+ , Node 12 []
+ ]
+ ]
+ , Node 13 [ Node 14 []]
+ ]
+ , Node 15 []
+ ]
diff --git a/chapters/tree_traversal/tree_traversal.md b/chapters/tree_traversal/tree_traversal.md
index 343ce5fd1..cf81368dc 100644
--- a/chapters/tree_traversal/tree_traversal.md
+++ b/chapters/tree_traversal/tree_traversal.md
@@ -1,4 +1,4 @@
-# Tree Traversal
+# Tree Traversal
Trees are naturally recursive data structures, and because of this, we cannot access their elements like we might access the elements of a vector or array. Instead, we need to use more interesting methods to work through each element. This is often called *Tree Traversal*, and there are many different ways to do this. For now, we will restrict the discussion to two common and related methods of tree traversal: *Depth-First* and *Breadth-First Search*. Note that trees vary greatly in shape and size depending on how they are used; however, they are composed primarily of nodes that house other, children nodes, like so:
@@ -23,6 +23,8 @@ This has not been implemented in your chosen language, so here is the Julia code
[import:3-7, lang:"julia"](code/julia/Tree.jl)
{% sample lang="rs"%}
[import:4-7, lang:"rust"](code/rust/tree.rs)
+{% sample lang="hs"%}
+[import:1-3, lang:"haskell"](code/haskell/TreeTraversal.hs)
{% endmethod %}
Because of this, the most straightforward way to traverse the tree might be recursive. This naturally leads us to the Depth-First Search (DFS) method:
@@ -48,6 +50,8 @@ This has not been implemented in your chosen language, so here is the Julia code
[import:9-16, lang:"julia"](code/julia/Tree.jl)
{% sample lang="rs"%}
[import:9-15 lang:"rust"](code/rust/tree.rs)
+{% sample lang="hs"%}
+[import:5-6, lang:"haskell"](code/haskell/TreeTraversal.hs)
{% endmethod %}
At least to me, this makes a lot of sense. We fight recursion with recursion! First, we first output the node we are on and then we call `DFS_recursive(...)` on each of its children nodes. This method of tree traversal does what its name implies: it goes to the depths of the tree first before going through the rest of the branches. In this case, the ordering looks like:
@@ -56,7 +60,7 @@ At least to me, this makes a lot of sense. We fight recursion with recursion! Fi
@@ -121,6 +127,8 @@ This has not been implemented in your chosen language, so here is the Julia code {% sample lang="rs"%} This has not been implemented in your chosen language, so here is the Julia code [import:28-43, lang:"julia"](code/julia/Tree.jl) +{% sample lang="hs"%} +[import:11-15, lang:"haskell"](code/haskell/TreeTraversal.hs) {% endmethod %}
@@ -130,7 +138,7 @@ This has not been implemented in your chosen language, so here is the Julia code The order here seems to be some mix of the other 2 methods and works through the binary tree from left to right. -Now, at this point, it might seem that the only way to search through a recursive data structure is with recusion, but this is not necessarily the case! Rather surprisingly, we can perform a DFS non-recursively by using a stack, which are data structures that hold multiple elements, but only allow you to interact with the very last element you put in. The idea here is simple: +Now, at this point, it might seem that the only way to search through a recursive data structure is with recursion, but this is not necessarily the case! Rather surprisingly, we can perform a DFS non-recursively by using a stack, which are data structures that hold multiple elements, but only allow you to interact with the very last element you put in. The idea here is simple: 1. Put the root node in the stack 2. Take it out and put in its children @@ -161,6 +169,9 @@ This has not been implemented in your chosen language, so here is the Julia code [import:45-56, lang:"julia"](code/julia/Tree.jl) {% sample lang="rs"%} [import:17-24, lang:"rust"](code/rust/tree.rs) +{% sample lang="hs"%} +This has not been implemented in your chosen language, so here is the Julia code +[import:45-56, lang:"julia"](code/julia/Tree.jl) {% endmethod %} All this said, there are a few details about DFS that might not be idea, depending on the situation. For example, if we use DFS on an incredibly long tree, we will spend a lot of time going further and further down a single branch without searching the rest of the data structure. In addition, it is not the natural way humans would order a tree if asked to number all the nodes from top to bottom. I would argue a more natural traversal order would look something like this: @@ -191,6 +202,8 @@ This has not been implemented in your chosen language, so here is the Julia code [import:58-69, lang:"julia"](code/julia/Tree.jl) {% sample lang="rs"%} [import:26-34, lang:"rust"](code/rust/tree.rs) +{% sample lang="hs"%} +[import:17-20, lang:"haskell"](code/haskell/TreeTraversal.hs) {% endmethod %} # Example Code @@ -225,13 +238,16 @@ Program.cs {% sample lang="rs"%} ### Rust [import, lang:"rust"](code/rust/tree.rs) +### Haskell +{% sample lang="hs"%} +[import, lang:"haskell"](code/haskell/TreeTraversal.hs) {% endmethod %} -$$ +$$ \newcommand{\d}{\mathrm{d}} \newcommand{\bff}{\boldsymbol{f}} \newcommand{\bfg}{\boldsymbol{g}} @@ -250,4 +266,3 @@ $$ \newcommand{\bfomega}{\boldsymbol{\omega}} \newcommand{\bftau}{\boldsymbol{\tau}} $$ -