Differenza tra `set`,` setq` e `setf` in Common Lisp?


166

Qual è la differenza tra "set", "setq" e "setf" in Common Lisp?


9
Il comportamento di questi viene risposto abbastanza bene nelle risposte, ma la risposta accettata ha un'etimologia probabilmente errata per la "f" in "setf". La risposta a Cosa significa f in setf? dice che è per "funzione" e fornisce riferimenti per eseguirne il backup.
Joshua Taylor,

Risposte:


169

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.


45
Il Lisp comune ha sempre avuto variabili lessicali. Devi parlare di un po 'di Lisp prima del Common Lisp.
Rainer Joswig,

4
Se SET e SETQ devono essere avviati da un successore di Common Lisp, dovranno essere sostituiti. Il loro uso nel codice di alto livello è limitato, ma il codice di basso livello (ad esempio, il codice SETF è implementato in) ne ha bisogno.
Svante,

13
c'è un motivo per cui hai scelto "macchina" come campo anziché qualcosa che potrebbe essere confuso come funzione dell'automobile?
drudru,

9
Questa risposta a Cosa significa f in setf? afferma che feffettivamente 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.
Joshua Taylor,

1
Riepilogo: setè una funzione. Quindi non conosce l'ambiente. setimpossibile vedere la variabile lessicale. Può impostare solo il valore-simbolo del suo argomento. setqnon viene più "impostato tra virgolette". Il fatto che setqsia una forma speciale, non una macro lo dimostra.
KIM Taegyoon,

142
(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

12
Trovo che la tua risposta sia più chiara di quella più votata. Molte grazie.
CDR

2
@Sourav, per favore non usare MAI la lettera "l" (ell) come variabile o simbolo nel codice di esempio. È troppo difficile distinguere visivamente dal numero 1.
DavidBooth,

No, ancora non capisco come (auto ls) possa essere un valore l o no. Capisci come tradurre CLisp in C? e come scrivere un interprete CLisp?
riprende il

@ user1952009 clisp è un'implementazione Common Lisp. Se si desidera fare riferimento alla lingua stessa, CL l'abbreviazione più utilizzata.
ssice,

2
@ user1952009 Dopo (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 = 5in C.
kyle il

21

setqè proprio come setcon un primo argomento citato - (set 'foo '(bar baz))è proprio come (setq foo '(bar baz)). setfd'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ò, setfprende 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.


1
Ha più senso per il nome setq. Facile da ricordare. Grazie.
CDR

17

È possibile utilizzare setfal posto di seto setqnon viceversa poiché setfpuò 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)

setfha la capacità aggiuntiva di impostare un membro dell'elenco foosu 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 defvarse 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))

13

Si può pensare SETed SETQessere 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 SETFespandesse in SETe SETQ.

* (macroexpand '(setf (symbol-value 'a) 10))

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Quindi SETe SETQvengono 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.


4

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))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.