11

For some reason Ruby seems to perform better when facing left recursion. For example:

def left_recursive_factorial(number)
  return 1 if number.zero?
  left_recursive_factorial(number.pred) * number
end

def right_recursive_factorial(number)
  return 1 if number.zero?
  number * right_recursive_factorial(number.pred)
end

When I call these methods with a value over 9000 () I get different results:

left_recursive_factorial(9001)
  # => factorial of 9001

right_recursive_factorial(9001)
  # => SystemStackError: stack level too deep
  #    from (pry):6:in `right_recursive_factorial'

I couldn't find any explanation for this behavior.

The only thing that seemed somewhat related was about LL() parsers having problems with left recursion and I guess you can flip it around, but I haven't dug much into it.

Could someone explain in a little more detail what causes left and right recursions to perform differently (generally and in Ruby specifically) and if you have the possibility to choose one or the other why would you pick it (and why was left chosen in Ruby)?

ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • It seems that ruby evaluates the right side of a multiplication before the left, and therefore the left version uses [tail recursion](https://en.wikipedia.org/wiki/Tail_call) so it doesn't have to add onto the stack. – clcto Apr 30 '14 at 17:45
  • @clcto: This doesn't look like tail call elimination. For one, the multiplication is the last operation in the function, not the recursive call. For another, you'll still blow the stack if you just up the number to 9500. – Chuck Apr 30 '14 at 17:54
  • 4
    @clcto Ruby most definitely evaluates operands and method arguments left-to-right, not right-to-left. Furthermore the order in which operands are evaluated is irrelevant with regards to whether something is tail recursive or not. The multiplication necessarily happens after the function call (because you can't multiply two numbers until you know both the numbers), so the method is not tail-recursive no matter which number gets evaluated first. And either way the standard Ruby interpreter does not optimize tail recursion. – sepp2k Apr 30 '14 at 17:55

1 Answers1

6

OK, I am not a YARV hacker of any sort, but here's the difference as I understand it. When you call a method, the sender pushes the method's arguments onto the stack, then the called method pops its arguments off and pushes its return value on. With the recursive call first, the number argument hasn't been pushed onto the stack yet, so the stack of each call takes slightly less space. This is why you can get a few more iterations out of that version, but not drastically more — you're looking at a few percent reduction in stack usage.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • This makes sense, but I was hoping for a little more detailed answer. For example why do python and php seem to be indifferent, while js shows similar behaviour. – ndnenkov May 06 '14 at 19:35
  • 1
    @ndn: This is highly dependent on the language implementation. For example, Python has a recursion limit, which is an actual value that the interpreter checks and refuses recursive calls if it has been reached. You can change it with `sys.setrecursionlimit()`. In PHP's case, I had to go grab a disassembler because I had no idea how it worked. It doesn't take the stack-machine approach of pushing arguments and then calling multiply. Instead, it actually gives the operands to `*` as operands of the `MUL` opcode, so the only difference between the functions is `MUL !0, $2` vs. `MUL $2, !0`. – Chuck May 06 '14 at 21:39