Skip to content

Instantly share code, notes, and snippets.

@tdg5
Last active September 30, 2015 12:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tdg5/ff140ae34a556a488c50 to your computer and use it in GitHub Desktop.
Save tdg5/ff140ae34a556a488c50 to your computer and use it in GitHub Desktop.
Better Object-Oriented Design Through... Tail-Call Optimization?
# Abstract UnionList base class
class AbstractUnionList
private :initialize
def length
how_many?(0)
end
def how_many?(counter)
raise NotImplementedError
end
end
# UnionList NullObject
class NullUnionList < AbstractUnionList
public :initialize
def how_many?(counter)
counter
end
end
# How one might intuitively implement UnionList
class UnionList < AbstractUnionList
attr_reader :adjoining
def initialize(adjoining)
@adjoining = adjoining
end
def how_many?(counter)
adjoining.how_many?(counter + 1)
end
end
# OO-design violating work around for SystemStackError
class IterativeUnionList < UnionList
def how_many?(counter)
remaining = self
count = 0
# Shouldn't have to know anything about NullUnionList :(
while !remaining.is_a?(NullUnionList)
count += 1
remaining = remaining.adjoining
end
count
end
end
# OO-design friendly and it works? Still my beating heart!
require "tco_method"
class TCOUnionList < UnionList
extend TCOMethod::Mixin
tco_method :how_many?
end
# A class to test each of the UnionList implementations
class UnionListTest
def test(class_under_test, length)
last = NullUnionList.new
length.times { last = class_under_test.new(last) }
msg = "class_under_test=#{class_under_test.name} length=#{length} result="
msg.concat(last.length == length ? "Success" : "Fail")
puts(msg)
rescue StandardError, SystemStackError => e
puts msg.concat("Fail error=#{e.class}")
end
end
# Let the testing begin!
test_case = UnionListTest.new
classes_to_test = [
UnionList,
IterativeUnionList,
TCOUnionList,
]
divider = "-----------------"
puts divider
# Test lists of length 1..10000
5.times do |pow|
length = 10 ** pow
classes_to_test.each do |class_under_test|
test_case.test(class_under_test, length)
end
puts divider
end
# -----------------
# class_under_test=UnionList length=1 result=Success
# class_under_test=IterativeUnionList length=1 result=Success
# class_under_test=TCOUnionList length=1 result=Success
# -----------------
# class_under_test=UnionList length=10 result=Success
# class_under_test=IterativeUnionList length=10 result=Success
# class_under_test=TCOUnionList length=10 result=Success
# -----------------
# class_under_test=UnionList length=100 result=Success
# class_under_test=IterativeUnionList length=100 result=Success
# class_under_test=TCOUnionList length=100 result=Success
# -----------------
# class_under_test=UnionList length=1000 result=Success
# class_under_test=IterativeUnionList length=1000 result=Success
# class_under_test=TCOUnionList length=1000 result=Success
# -----------------
# class_under_test=UnionList length=10000 result=Fail error=SystemStackError
# class_under_test=IterativeUnionList length=10000 result=Success
# class_under_test=TCOUnionList length=10000 result=Success
# -----------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment