Skip to content

Instantly share code, notes, and snippets.

@tdg5
Last active August 29, 2015 14:17
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/18e628bf987b2baf475a to your computer and use it in GitHub Desktop.
Save tdg5/18e628bf987b2baf475a to your computer and use it in GitHub Desktop.
PoC of detecting tail call optimization by monitoring stack size via a lambda callback
require 'tco_method'
module StackBuster
def self.fact_yielder(n, acc = 1, &block)
block.call(acc)
n <= 1 ? acc : fact_yielder(n - 1, n * acc, &block)
end
end
puts "StackBuster:"
StackBuster.fact_yielder(10) do |acc|
puts caller.length
end
module StackFloater
extend TCOMethod::Mixin
def self.fact_yielder(n, acc = 1, &block)
block.call(acc)
n <= 1 ? acc : fact_yielder(n - 1, n * acc, &block)
end
tco_module_method :fact_yielder
end
puts "\nStackFloater:"
StackFloater.fact_yielder(10) do |acc|
puts caller.length
end
# Output:
# #######
#
# StackBuster:
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10
# 11
# 12
#
# StackFloater:
# 3
# 3
# 3
# 3
# 3
# 3
# 3
# 3
# 3
# 3
## Or, alternately:
require "tco_method"
module FibYielder
def self.fib_yielder(index, back_one = 1, back_two = 0, &block)
yield back_two if index > 0
index < 1 ? back_two : fib_yielder(index - 1, back_one + back_two, back_one, &block)
end
end
2.times do |idx|
initial_length = nil
is_tco = FibYielder.fib_yielder(10) do |fib|
if initial_length.nil?
initial_length = caller.length
else
break initial_length == caller.length
end
end
puts "Iteration #{idx + 1} is tco? : #{is_tco}"
next unless idx.zero?
FibYielder.extend(TCOMethod::Mixin)
FibYielder.tco_module_method(:fib_yielder)
end
# Iteration 1 is tco? : false
# Iteration 2 is tco? : true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment