Created
August 21, 2022 22:15
-
-
Save JoelQ/02f3ef9f61bebc7c8e5ea67d10ed92c6 to your computer and use it in GitHub Desktop.
Demonstration of how using `Enumerator` makes it nicer to work with a data structure that has multiple valid traversals such as a binary tree
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Tree | |
attr_reader :val, :left, :right | |
def self.empty | |
EmptyTree.new | |
end | |
def self.leaf(val) | |
new(val, empty, empty) | |
end | |
def initialize(val, left, right) | |
@val = val | |
@left = left | |
@right = right | |
end | |
# ==== Traversals ==== | |
# | |
# These methods provide different ways of traversing a tree. See | |
# https://en.wikipedia.org/wiki/Tree_traversal#Depth-first_search for more on | |
# the different ways a tree can be traversed. | |
# | |
# The methods can be called with a block and each item will be yielded in that | |
# given order: | |
# | |
# tree.preorder { |n| puts n } | |
# | |
# When called without a block they return a `Enumerator`. This allows calling | |
# any of the `Enumerable` methods for that given traversal such as: | |
# | |
# tree.inorder.map { |n| n * 2 } | |
# tree.postorder.find(&:even?) | |
# Depth-first search, pre-order. Given this tree: | |
# | |
# 1 | |
# / \ | |
# 2 5 | |
# / \ / \ | |
# 3 4 6 7 | |
# | |
# A pre-order traversal will go: | |
# 1, 2, 3, 4, 5, 6, 7 | |
def preorder(&block) | |
return to_enum(:preorder) unless block_given? | |
block.call(val) | |
left.preorder(&block) | |
right.preorder(&block) | |
end | |
# Depth-first search, in-order. Given this tree: | |
# | |
# 1 | |
# / \ | |
# 2 5 | |
# / \ / \ | |
# 3 4 6 7 | |
# | |
# An in-order traversal will go: | |
# 3, 2, 4, 1, 6, 5, 7 | |
def inorder(&block) | |
return to_enum(:inorder) unless block_given? | |
left.inorder(&block) | |
block.call(val) | |
right.inorder(&block) | |
end | |
# Depth-first search, post-order. Given this tree: | |
# | |
# 1 | |
# / \ | |
# 2 5 | |
# / \ / \ | |
# 3 4 6 7 | |
# | |
# A post-order traversal will go: | |
# 3, 4, 2, 6, 7, 5, 1 | |
def postorder(&block) | |
return to_enum(:postorder) unless block_given? | |
left.postorder(&block) | |
right.postorder(&block) | |
block.call(val) | |
end | |
end | |
class EmptyTree | |
def preorder | |
end | |
def postorder | |
end | |
def inorder | |
end | |
end | |
# 1 | |
# / \ | |
# 2 5 | |
# / \ / \ | |
# 3 4 6 7 | |
tree = | |
Tree.new( | |
1, | |
Tree.new( | |
2, | |
Tree.leaf(3), | |
Tree.leaf(4), | |
), | |
Tree.new( | |
5, | |
Tree.leaf(6), | |
Tree.leaf(7), | |
), | |
) | |
# (2 * 3) + (12 / 6) | |
expression = | |
Tree.new( | |
:+, | |
Tree.new(:*, Tree.leaf(2), Tree.leaf(3)), | |
Tree.new(:/, Tree.leaf(12), Tree.leaf(6)), | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@camertron that's a great tip! Thanks for sharing!