mutual_recursion/lib/mutual_recursion.rb

58 lines
1.2 KiB
Ruby

# frozen_string_literal: true
module MutualRecursion
class TailCall
attr_reader :value, :block
def initialize(value = nil, &block)
@value = value
@block = block
end
##
# Invoke this tail call. Only the returned value from the initial call to
# the recursive function should be invoked.
#
# @return [Object] the terminal_value of this tail call
def invoke
self.then do |tail|
while tail.block
tail = tail.block.call
raise MissingTailCallError unless tail.is_a?(TailCall)
end
tail.value
end
end
end
class MissingTailCallError < StandardError
def initialize(msg = 'expected a tail call')
super
end
end
module_function
##
# Make a direct or indirect recursive call in tail position.
#
# @yieldreturn [MutualRecursion::TailCall] a tail call
# @return [MutualRecursion::TailCall] a non terminal tail call
def tail_call
TailCall.new { yield }
end
##
# Indicates that the recursion has ended with the provided value.
#
# @param [Object] value the terminal value
# @return [MutualRecursion::TailCall] a terminal tail call that will return the given value
def terminal_value(value)
TailCall.new(value)
end
end