Qual è la differenza tra "set", "setq" e "setf" in Common Lisp?
Qual è la differenza tra "set", "setq" e "setf" in Common Lisp?
Risposte:
In origine, in Lisp, non c'erano variabili lessicali - solo dinamiche. E non c'erano SETQ o SETF, solo la funzione SET.
Ciò che è ora scritto come:
(setf (symbol-value '*foo*) 42)
è stato scritto come:
(set (quote *foo*) 42)
che alla fine è stato abbreviato in SETQ (SET Quoted):
(setq *foo* 42)
Quindi si sono verificate variabili lessicali e SETQ è stato utilizzato anche per l'assegnazione a esse - quindi non era più un semplice wrapper per SET.
Successivamente, qualcuno ha inventato SETF (SET Field) come un modo generico di assegnare valori a strutture di dati, per rispecchiare i valori l di altre lingue:
x.car := 42;
sarebbe scritto come
(setf (car x) 42)
Per simmetria e generalità, SETF ha anche fornito la funzionalità di SETQ. A questo punto sarebbe stato corretto affermare che SETQ era una primitiva di basso livello e SETF un'operazione di alto livello.
Quindi sono avvenute le macro dei simboli. Affinché le macro di simboli potessero funzionare in modo trasparente, si è capito che SETQ avrebbe dovuto agire come SETF se la "variabile" assegnata fosse in realtà una macro di simboli:
(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))
foo => 42
(setq foo 13)
foo => 13
*hidden* => (13 . 42)
Quindi arriviamo ai giorni nostri: SET e SETQ sono resti atrofizzati di dialetti più vecchi e probabilmente verranno avviati da eventuali successori di Common Lisp.
f
effettivamente sta per funzione , non campo (o forma , per quella materia), e fornisce riferimenti, quindi mentre setf per campo ha un senso, sembra che potrebbe non essere corretto.
set
è una funzione. Quindi non conosce l'ambiente. set
impossibile vedere la variabile lessicale. Può impostare solo il valore-simbolo del suo argomento. setq
non viene più "impostato tra virgolette". Il fatto che setq
sia una forma speciale, non una macro lo dimostra.
(set ls '(1 2 3 4)) => Error - ls has no value
(set 'ls '(1 2 3 4)) => OK
(setq ls '(1 2 3 4)) => OK - make ls to (quote ls) and then have the usual set
(setf ls '(1 2 3 4)) => OK - same as setq so far BUT
(setf (car ls) 10) => Makes ls '(10 2 3 4) - not duplicated by setq/set
(setq ls '(((1))))
, (setf (car (car (car ls))) 5)
è un comportamento indefinito, perché il valore di ls
è costante (come modificare una stringa letterale in C). Dopo (setq ls (list (list (list 1))))
, (setf (car (car (car ls))) 5)
funziona proprio come ls->val->val->val = 5
in C.
setq
è proprio come set
con un primo argomento citato - (set 'foo '(bar baz))
è proprio come (setq foo '(bar baz))
. setf
d'altra parte, è davvero sottile - è come una "indiretta". Suggerisco http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html come un modo migliore per iniziare a comprenderlo di quanto qualsiasi risposta qui possa dare ... in breve, però, setf
prende il primo argomento come "riferimento", in modo che ad esempio (aref myarray 3)
funzionerà (come il primo argomento setf
) per impostare un elemento all'interno di un array.
È possibile utilizzare setf
al posto di set
o setq
non viceversa poiché setf
può anche impostare il valore di singoli elementi di una variabile se la variabile ha singoli elementi. Vedi gli exaples di seguito:
Tutti e quattro gli esempi assegneranno l'elenco (1, 2, 3) alla variabile denominata foo.
(set (quote foo) (list 1 2 3)) ;foo => (1 2 3)
(1 2 3)
(set 'foo '(1 2 3)) ;foo => (1 2 3) same function, simpler expression
(1 2 3)
(setq foo '(1 2 3)) ;foo => (1 2 3) similar function, different syntax
(1 2 3)
(setf foo '(1 2 3)) ;foo => (1 2 3) more capable function
(1 2 3)
setf
ha la capacità aggiuntiva di impostare un membro dell'elenco foo
su un nuovo valore.
foo ;foo => (1 2 3) as defined above
(1 2 3)
(car foo) ;the first item in foo is 1
1
(setf (car foo) 4) ;set or setq will fail since (car foo) is not a symbol
4
foo ;the fist item in foo was set to 4 by setf
(4 2 3)
Tuttavia, è possibile definire una macro di simboli che reinventa un singolo elemento all'interno foo
(define-symbol-macro foo-car (car foo)) ; assumes FOO => (1 2 3)
FOO-CAR
foo-car ;foo-car is now a symbol for the 1st item in foo
1
(setq foo-car 4) ;set or setq can set the symbol foo-car
4
foo ;Lisp macros are so cool
(4 2 3)
Puoi usarlo defvar
se non hai già definito la variabile e non vuoi dargli un valore più avanti nel tuo codice.
(defvar foo2)
(define-symbol-macro foo-car (car foo2))
Si può pensare SET
ed SETQ
essere costrutti di basso livello.
SET
può impostare il valore dei simboli.
SETQ
può impostare il valore delle variabili.
Poi SETF
c'è una macro, che fornisce molti tipi di impostazioni: simboli, variabili, elementi dell'array, slot di istanza, ...
Per simboli e variabili si può pensare come se si SETF
espandesse in SET
e SETQ
.
* (macroexpand '(setf (symbol-value 'a) 10))
(SET 'A 10)
* (macroexpand '(setf a 10))
(SETQ A 10)
Quindi SET
e SETQ
vengono utilizzati per implementare alcune delle funzionalità di SETF
, che è il costrutto più generale. Alcune delle altre risposte raccontano la storia leggermente più complessa, quando prendiamo in considerazione le macro dei simboli.
Vorrei aggiungere alle risposte precedenti che setf è una macro che chiama una funzione specifica a seconda di ciò che è stato passato come primo argomento. Confronta i risultati dell'espansione macro di setf con diversi tipi di argomenti:
(macroexpand '(setf a 1))
(macroexpand '(setf (car (list 3 2 1)) 1))
(macroexpand '(setf (aref #(3 2 1) 0) 1))
Per alcuni tipi di argomenti verrà chiamata "setf function":
(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))