In primo luogo, dovresti capire di cosa tratta la coda.
Le chiamate tail sono chiamate che non consumano stack. Ora devi riconoscere quando stai consumando lo stack.
Prendiamo l'esempio del fattoriale:
(defun factorial (n)
(if (= n 1)
1
(* n (factorial (- n 1)))))
Ecco la non-coda ricorsiva attuazione fattoriale. Perché? Questo perché oltre al ritorno da fattoriale, c'è un calcolo in sospeso.
(* n ..)
Quindi si impilano n ogni volta che si chiama fattoriale. Ora scriviamo la coda fattoriale ricorsiva:
(defun factorial-opt (n &key (result 1))
(if (= n 1)
result
(factorial-opt (- n 1) :result (* result n))))
Qui, il risultato viene passato come argomento della funzione. Quindi stai anche consumando stack, ma la differenza è che le dimensioni dello stack rimangono costanti. Quindi, il compilatore può ottimizzarlo usando solo registri e lasciando lo stack vuoto.
Il factorial-opt
è quindi più veloce, ma è meno leggibile. factorial
è limitato alle dimensioni della pila non lo sarà factorial-opt
. Quindi dovresti imparare a riconoscere la funzione ricorsiva della coda per sapere se la ricorsione è limitata.
Potrebbe esserci qualche tecnica del compilatore per trasformare una funzione ricorsiva non di coda in una coda ricorsiva. Forse qualcuno potrebbe indicare qualche link qui.
Godere la ricorsione è come godersi i martelli. A volte è il miglior strumento disponibile, ma quando vuoi mettere una vite, prendi un cacciavite. – Xach
Mi piace l'idea della ricorsione. Mi affascina Tuttavia, non lo userei mai in qualsiasi cosa commerciale. Certo, rende il codice più facile da scrivere, ma a scapito dell'efficienza e del consumo di memoria. Preferisco le versioni iterative, per essere al sicuro. –