2019-01-26 14:09:00 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'minitest/autorun'
|
|
|
|
require 'minitest/pride'
|
|
|
|
|
|
|
|
require_relative '../lib/mutual_recursion'
|
|
|
|
|
|
|
|
class InventoryTest < Minitest::Test
|
2019-01-27 11:55:52 -05:00
|
|
|
include MutualRecursion
|
|
|
|
|
2019-01-27 12:01:23 -05:00
|
|
|
def one(x, y = 0)
|
|
|
|
return terminal_value(y) if x.negative?
|
|
|
|
|
|
|
|
tail_call { two(x, y + 1) }
|
|
|
|
end
|
|
|
|
|
2019-01-27 13:34:26 -05:00
|
|
|
def two(x, y = 0)
|
2019-01-27 12:01:23 -05:00
|
|
|
tail_call { one(x - 1, y) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def direct(x, y = 0)
|
|
|
|
return terminal_value(y) if x.negative?
|
|
|
|
|
|
|
|
tail_call { direct(x - 1, y + 1) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def lambda_returning
|
|
|
|
terminal_value(-> { 24 })
|
|
|
|
end
|
|
|
|
|
|
|
|
def proc_returning(x, y = 0)
|
|
|
|
return terminal_value(proc { "|#{y}|" }) if x.negative?
|
|
|
|
|
|
|
|
tail_call { proc_returning(x - 1, y + 1) }
|
|
|
|
end
|
|
|
|
|
2019-01-27 13:34:26 -05:00
|
|
|
def inauthentic_tail_call
|
2019-01-27 12:01:23 -05:00
|
|
|
tail_call do
|
|
|
|
Class.new do
|
|
|
|
attr_reader :value, :block
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
@value = 99
|
2019-01-27 13:34:26 -05:00
|
|
|
@block = proc { MutualRecursion.terminal_value(100) }
|
2019-01-27 12:01:23 -05:00
|
|
|
end
|
|
|
|
end.new
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-01-26 14:09:00 -05:00
|
|
|
def test_terminal_value
|
|
|
|
tail = terminal_value(42)
|
|
|
|
assert_equal(42, tail.invoke)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_single_recursion
|
|
|
|
tail = tail_call { terminal_value(42) }
|
|
|
|
assert_equal(42, tail.invoke)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_several_recursions
|
|
|
|
tail = tail_call { tail_call { tail_call { terminal_value(42) } } }
|
|
|
|
assert_equal(42, tail.invoke)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_mutual_tail_recursion
|
2019-01-27 13:34:26 -05:00
|
|
|
tail_one = one(50_000)
|
|
|
|
tail_two = two(50_000)
|
|
|
|
assert_equal(50_001, tail_one.invoke)
|
|
|
|
assert_equal(50_000, tail_two.invoke)
|
2019-01-26 14:09:00 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_direct_tail_recursion
|
|
|
|
tail = direct(50_000)
|
|
|
|
assert_equal(50_001, tail.invoke)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_recursive_function_can_return_lambda
|
|
|
|
tail = lambda_returning
|
|
|
|
assert_kind_of(Proc, tail.invoke)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_recursive_function_can_return_proc
|
2019-01-26 14:41:19 -05:00
|
|
|
tail = proc_returning(20)
|
2019-01-26 14:09:00 -05:00
|
|
|
assert_kind_of(Proc, tail.invoke)
|
|
|
|
end
|
|
|
|
|
2019-01-27 13:34:26 -05:00
|
|
|
def test_inauthentic_tail_call_detected
|
|
|
|
tail = inauthentic_tail_call
|
2019-01-27 11:55:52 -05:00
|
|
|
assert_raises(MissingTailCallError) { tail.invoke }
|
2019-01-26 14:09:00 -05:00
|
|
|
end
|
|
|
|
|
2019-01-27 11:55:52 -05:00
|
|
|
def test_module_functions
|
|
|
|
tail = MutualRecursion.tail_call { MutualRecursion.terminal_value(42) }
|
|
|
|
assert_equal(42, tail.invoke)
|
2019-01-26 14:09:00 -05:00
|
|
|
end
|
|
|
|
end
|