Per qualche motivo, Ruby sembra funzionare meglio quando si trova di fronte alla ricorsione a sinistra. Per esempio:Ruby left vs right ricorsione
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
Quando chiamo questi metodi con un valore di oltre 9000() ottengo risultati diversi:
left_recursive_factorial(9001)
# => factorial of 9001
right_recursive_factorial(9001)
# => SystemStackError: stack level too deep
# from (pry):6:in `right_recursive_factorial'
non ho potuto trovare alcuna spiegazione per questo comportamento.
L'unica cosa che sembrava in qualche modo correlata era circa i parser LL()
che hanno problemi con la ricorsione a sinistra e credo che si possa capovolgere, ma non ho scavato molto dentro.
Qualcuno potrebbe spiegare un po 'più in dettaglio ciò che causa la ricorsione destra e quella sinistra in modo diverso (in generale e in Ruby in particolare) e se si ha la possibilità di scegliere l'uno o l'altro perché lo si dovrebbe scegliere (e perché è stato lasciato scelto in Ruby)?
Sembra che Ruby valuti il lato destro di una moltiplicazione prima di sinistra, e quindi la versione di sinistra usa [ricorsione di coda] (https://en.wikipedia.org/wiki/Tail_call) in modo che non debba aggiungere nello stack. – clcto
@clcto: non sembra un'eliminazione della coda. Per uno, la moltiplicazione è l'ultima operazione nella funzione, non la chiamata ricorsiva. Per un altro, continuerete a saltare lo stack se solo il numero fino a 9500. – Chuck
@clcto Ruby valuta decisamente gli operandi e gli argomenti del metodo da sinistra a destra, non da destra a sinistra. Inoltre, l'ordine in cui vengono valutati gli operandi è irrilevante rispetto al fatto che qualcosa sia ricorsivo o meno. La moltiplicazione avviene necessariamente dopo la chiamata alla funzione (poiché non è possibile moltiplicare due numeri finché non si conoscono entrambi i numeri), quindi il metodo non è ricorsivo in coda, indipendentemente dal numero che viene valutato per primo. In ogni caso, l'interprete standard di Ruby non ottimizza la ricorsione della coda. – sepp2k