Skip to content

Instantly share code, notes, and snippets.

@JoelQ
Created August 21, 2022 22:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoelQ/02f3ef9f61bebc7c8e5ea67d10ed92c6 to your computer and use it in GitHub Desktop.
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
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