Ruby, of course, does have recursion, just like any other high-level programming language. This works fine as long as recursion depth is not too high, but if it is, you will catch a stack overflow:
#!/usr/bin/ruby2.0
def rec_naive(i)
return 1 if i==1
rec_naive(i-1) + i
end
puts rec_naive(10000) #(Stack size: ~9360)
#==> test.rb:3: stack level too deep (SystemStackError)
The most obvious solution that comes to mind is to simply increase the stack size. Unfortunately, answers I found on the subject suggested altering the OS state in one or another way - tinkering with Ruby interpreter source code, ulimit
, compile flags and the like - which is beyound pure Ruby and, of course, is not always possible, especially in secure environments. So, the less obvious solutions coming to mind are to rewrite the offending function in a non-recursive way or reimplement a calling stack:
# Recursion-free way
def rec_norecurse(i)
acc = 0
(1..i).each do |n|
acc += n
end
return acc
end
puts rec_norecurse(100)
# Reimplementing the call stack
StackFrame = Struct.new(:state, :args)
def rec_customstack(stack)
lastresult = nil
until stack.empty?
frame = stack.last
state, args = frame.state, frame.args
i = args[0]
case state
when :entrance_point
if i==1
#-- return 1 #--
lastresult = 1
stack.pop
#---------------
else
#-- rec(i-1) #--
stack.last.state = :returned_from_recursion
stack << StackFrame.new(:entrance_point, [i-1])
#---------------
end
when :returned_from_recursion
#-- return rec_result+i #--
lastresult = lastresult + i
stack.pop
#--------------------------
end
end
return lastresult
end
customstack = [StackFrame.new(:entrance_point, [100])]
puts rec_customstack(customstack)
However, rewriting even not too complex functions in this way can be a tedious task, and the resulting code seems to be too cluttered and obscured in comparsion to original one. I wanted to involve some metaprogramming and write some sort of a "wrapper", which could make the wrapped function behave properly with deep recursion while being clean enough, even if not looking exactly as unwrapped one would. I implemented a solution with Fibers, which initially seemed good enough, but then I encountered some unexpected difficulties (see the related question for details).
So, I'm looking for a right and clean - with as least cluttering and obscuring as possible - way to implement very deep recursion calls without hurting the performance too much.