Perché questa funzione restituisce un valore diverso ogni volta?

Qualcuno può spiegare il seguente comportamento? In particolare, perché la funzione restituisce ogni volta un elenco diverso? Perché la some-list non è inizializzata su '(0 0 0) ogni volta che viene chiamata la funzione?

 (defun foo () (let ((some-list '(0 0 0))) (incf (car some-list)) some-list)) 

Produzione:

 > (foo) (1 0 0) > (foo) (2 0 0) > (foo) (3 0 0) > (foo) (4 0 0) 

Grazie!

MODIFICARE:

Inoltre, qual è il modo consigliato di implementare questa funzione, supponendo che voglio che la funzione emetta '(1 0 0) ogni volta?

'(0 0 0) è un object letterale, che si presume essere una costante (sebbene non protetta dalla modifica). Quindi stai modificando efficacemente lo stesso object ogni volta. Per creare oggetti diversi in ogni funzione, utilizzare la chiamata (list 0 0 0) .

Quindi, a meno che tu non sappia, cosa stai facendo, dovresti sempre usare le liste letterali (come '(0 0 0) ) solo come costanti.

Nota a margine, definendo questa funzione nel file REPL sbcl si ottiene il seguente avviso:

  caught WARNING: Destructive function SB-KERNEL:%RPLACA called on constant data. See also: The ANSI Standard, Special Operator QUOTE The ANSI Standard, Section 3.2.2.3 

Che dà un buon suggerimento per il problema in questione.

'(0 0 0) nel codice è letterale. La modifica di questi dati ha un comportamento indefinito. Le implementazioni comuni di Lisp potrebbero non rilevarlo in fase di runtime (a meno che i dati non vengano posizionati ad esempio in uno spazio di memoria di sola lettura). Ma può avere effetti indesiderati.

  • si vede che questi dati possono essere (e spesso lo sono) condivisi tra varie invocazioni della stessa funzione

  • uno degli errori più sottili possibili è questo: Common Lisp è stato definito con varie ottimizzazioni che possono essere fatte da un compilatore in mente. Ad esempio, un compilatore può riutilizzare i dati:

Esempio:

 (let ((a '(1 2 3)) (b '(1 2 3))) (list ab)) 

Nello snippet di codice sopra il compilatore può rilevare che i dati letterali di b sono EQUAL . Potrebbe quindi avere entrambe le variabili puntare agli stessi dati letterali. La modifica potrebbe funzionare, ma la modifica è visibile da a e b .

Riepilogo: la modifica dei dati letterali è una fonte di numerosi bug sottili. Evitalo se ansible. Quindi è necessario utilizzare nuovi oggetti dati. Consing in generale significa l’allocazione di nuove strutture dati nuove in fase di runtime.

Volevo scriverne uno anch’io, ma ne ho trovato uno buono online:

CommonLisp ha funzioni di prima class, cioè le funzioni sono oggetti che possono essere creati in fase di runtime e passati come argomenti ad altre funzioni. –AlainPicard Anche queste funzioni di prima class hanno il loro stato, quindi sono funtori. Tutte le funzioni Lisp sono funtori; non c’è separazione tra funzioni che sono “solo codice” e “oggetti funzione”. Lo stato assume la forma di associazioni di variabili lessicali catturate. Non è necessario utilizzare LAMBDA per acquisire i binding; un DEFUN di primo livello può farlo anche: (let ((private-variable 42)) (defun foo () …))

Il codice al posto di … vede la variabile privata nel suo ambito lessicale. C’è un’istanza di questa variabile associata all’unico object funzione che è globalmente legato al simbolo FOO; la variabile viene catturata nel momento in cui viene valutata l’espressione DEFUN. Questa variabile agisce quindi come una variabile statica in C. Oppure, alternativamente, si può pensare a FOO come un object “singleton” con una “variabile di istanza”. –KazKylheku

Rif. http://c2.com/cgi/wiki?CommonLisp