Stai cercando ricorsione di coda. Al momento la funzione è definita come
(defun my-length (list)
(if list
(1+ (my-length (cdr list)))
0))
Si noti che dopo my-length
stessa ha chiamato, ha bisogno di aggiungere uno al risultato prima di passare tale valore alla sua funzione di chiamata. Questa necessità di modificare il valore prima di restituirlo significa che è necessario allocare un nuovo stack frame per ogni chiamata, lo spazio utilizzato è proporzionale alla lunghezza dell'elenco. Questo è ciò che causa uno stack overflow su lunghi elenchi.
È possibile riscrivere in modo da utilizzare una funzione di supporto
(defun helper (acc list)
(if list
(helper (1+ acc) (cdr list))
acc))
(defun my-length (list)
(helper 0 list))
la funzione di supporto richiede due parametri, un parametro accumuloacc
, che memorizza la lunghezza della lista fino ad ora, e un elenco list
che è la lista di cui stiamo calcolando la lunghezza.
Il punto importante è che helper
viene scritto in modo ricorsivo, il che significa che chiamare se stesso è l'ultima cosa che fa. Ciò significa che non è necessario allocare un nuovo stack frame ogni volta che la funzione chiama se stesso - dal momento che il risultato finale verrà passato solo a monte della catena di frame dello stack, è possibile farla franca sovrascrivendo il vecchio stack frame con una nuova, quindi la tua funzione ricorsiva può operare in uno spazio costante.
Questa forma di trasformazione del programma - da una definizione ricorsiva (ma non a coda ricorsiva) a una definizione equivalente utilizzando una funzione di helper ricorsiva in coda è un idioma importante nella programmazione funzionale - uno che vale la pena trascorrere un po 'di tempo comprensione.
fonte
2013-03-07 11:05:11
http://docs.racket-lang.org/guide/Lists__Iteration__and_Recursion.html –
> * Quindi, o non si aspettano di avere una lista che a lungo in Lisp * Sapete che c'è una funzione 'length' , destra? Ecco perché hai chiamato il tuo "my-length". – Kaz