Z jakiegoś powodu Ruby wydaje się lepiej spisywać się w obliczu lewej rekursji. Na przykład:Ruby left vs right recursion
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
Kiedy ja nazywam te metody o wartości ponad 9000() uzyskać różne wyniki:
left_recursive_factorial(9001)
# => factorial of 9001
right_recursive_factorial(9001)
# => SystemStackError: stack level too deep
# from (pry):6:in `right_recursive_factorial'
nie mogłem znaleźć żadnego wytłumaczenia dla takiego zachowania.
Jedyną rzeczą, która wydawała się być nieco spokrewniona, były parsery LL()
mające problemy z lewą rekursją i myślę, że można je odwrócić, ale nie włożyłem w to wiele.
Czy ktoś mógłby dokładniej wyjaśnić, co powoduje, że lewe i prawe rekursje działają inaczej (ogólnie iw Ruby) i czy masz możliwość wyboru jednego lub drugiego, dlaczego wybrałbyś (i dlaczego zostało wybrany w Ruby)?
Wygląda na to, że rubin ocenia prawą stronę mnożenia przed lewą, a zatem lewa wersja używa [rekursji ogonowej] (https://en.wikipedia.org/wiki/Tail_call), więc nie musi dodaj do stosu. – clcto
@clcto: To nie wygląda na eliminację ogona. Po pierwsze, mnożenie jest ostatnią operacją w funkcji, a nie wywołaniem rekursywnym. Po drugie, nadal będziesz wysadzał stos, jeśli podniosłeś liczbę do 9500. – Chuck
@clcto Ruby zdecydowanie ocenia argumenty operacji i metody od lewej do prawej, a nie od prawej do lewej. Ponadto kolejność, w jakiej analizowane są operandy, jest nieistotna w odniesieniu do tego, czy coś jest rekurencyjne czy nie. Mnożenie ma miejsce po wywołaniu funkcji (ponieważ nie możesz pomnożyć dwóch liczb, dopóki nie poznasz obu liczb), więc metoda nie jest rekursywna, bez względu na to, która liczba zostanie oceniona jako pierwsza. I tak czy inaczej standardowy interpreter Ruby nie optymalizuje rekurencji ogona. – sepp2k