Différence entre `set`,` setq` et `setf` en Common Lisp?

166

Quelle est la différence entre "set", "setq" et "setf" en Common Lisp?

Richard Hoskins
la source
9
Le comportement de ceux-ci est assez bien répondu dans les réponses, mais la réponse acceptée a une étymologie peut-être erronée pour le "f" dans "setf". La réponse à Que signifie le f dans setf? dit que c'est pour "fonction", et fournit des références pour le sauvegarder.
Joshua Taylor

Réponses:

169

À l'origine, en Lisp, il n'y avait pas de variables lexicales - seulement des variables dynamiques. Et il n'y avait ni SETQ ni SETF, juste la fonction SET.

Ce qui s'écrit maintenant:

(setf (symbol-value '*foo*) 42)

a été écrit comme:

(set (quote *foo*) 42)

qui a finalement été abrégé en SETQ (SET cité):

(setq *foo* 42)

Ensuite, les variables lexicales sont apparues et SETQ a également été utilisé pour leur attribution - ce n'était donc plus un simple wrapper autour de SET.

Plus tard, quelqu'un a inventé SETF (SET Field) comme moyen générique d'attribuer des valeurs aux structures de données, pour refléter les valeurs l d'autres langages:

x.car := 42;

serait écrit comme

(setf (car x) 42)

Pour la symétrie et la généralité, SETF a également fourni la fonctionnalité de SETQ. À ce stade, il aurait été correct de dire que SETQ était une primitive de bas niveau et SETF une opération de haut niveau.

Ensuite, des macros de symboles se sont produites. Pour que les macros de symboles puissent fonctionner de manière transparente, il a été réalisé que SETQ devrait agir comme SETF si la "variable" affectée était vraiment une macro de symboles:

(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))

foo => 42

(setq foo 13)

foo => 13

*hidden* => (13 . 42)

Nous arrivons donc aujourd'hui: SET et SETQ sont des restes atrophiés d'anciens dialectes, et seront probablement démarrés à partir d'éventuels successeurs de Common Lisp.

programmeur de pile
la source
45
Common Lisp avait toujours des variables lexicales. Vous devez parler de Lisp avant Common Lisp.
Rainer Joswig
4
Si SET et SETQ doivent être démarrés à partir d'un successeur Common Lisp, ils devront être remplacés. Leur utilisation dans le code de haut niveau est limitée, mais le code de bas niveau (par exemple, le code SETF est implémenté dans) en a besoin.
Svante
13
y a-t-il une raison pour laquelle vous avez choisi «voiture» comme champ au lieu de quelque chose qui pourrait être confondu avec la fonction voiture?
drudru
9
Cette réponse à Que signifie le f dans setf? prétend que le freprésente en fait une fonction , pas un champ (ou un formulaire , d'ailleurs), et fournit des références, donc bien que le setf pour champ ait un certain sens, il semble que ce ne soit pas correct.
Joshua Taylor
1
Résumé: setest une fonction. Ainsi, il ne connaît pas l'environnement. setne peut pas voir la variable lexicale. Il ne peut définir que la valeur de symbole de son argument. setqn'est plus "set quoted". Le fait qu'il setqs'agisse d'une forme spéciale et non d'une macro le montre.
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
Sourav
la source
12
Je trouve que votre réponse est plus claire que celle qui a été la mieux votée. Merci beaucoup.
CDR
2
@Sourav, veuillez NE JAMAIS utiliser la lettre «l» (ell) comme variable ou symbole dans un exemple de code. Il est trop difficile de distinguer visuellement le chiffre 1.
DavidBooth
Non, je ne comprends toujours pas comment (car ls) peut être une valeur l ou non. Comprenez-vous comment traduire CLisp en C? et comment écrire un interpréteur CLisp?
Reuns
@ user1952009 clisp est une implémentation Common Lisp. Si vous voulez faire référence à la langue elle-même, CL l'abréviation la plus utilisée.
ssice
2
@ user1952009 Après (setq ls '(((1)))), (setf (car (car (car ls))) 5)est un comportement indéfini, car la valeur de lsest constante (comme la modification d'un littéral de chaîne en C). Après (setq ls (list (list (list 1)))), (setf (car (car (car ls))) 5)fonctionne comme ls->val->val->val = 5dans C.
kyle
21

setqest juste comme setavec un premier argument cité - (set 'foo '(bar baz))est juste comme (setq foo '(bar baz)). setf, d'autre part, est en effet subtile - c'est comme une "indirection". Je suggère http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html comme un meilleur moyen de commencer à le comprendre que toute réponse ici peut donner ... en bref, cependant, setfprend le premier argument comme "référence", de sorte que par exemple (aref myarray 3)fonctionnera (comme le premier argument à setf) pour définir un élément dans un tableau.

Alex Martelli
la source
1
Cela a plus de sens pour le nom setq. Facile à retenir. Merci.
CDR
17

Vous pouvez utiliser setfà la place setou setqmais pas l'inverse, car vous setfpouvez également définir la valeur des éléments individuels d'une variable si la variable a des éléments individuels. Voir les exemples ci-dessous:

Les quatre exemples attribueront la liste (1, 2, 3) à la variable nommée 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)

setfa la capacité supplémentaire de définir un membre de la liste dans fooune nouvelle valeur.

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)

Cependant, vous pouvez définir une macro de symboles qui représente un seul élément dans 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)

Vous pouvez utiliser defvarsi vous n'avez pas déjà défini la variable et ne souhaitez pas lui donner de valeur plus tard dans votre code.

(defvar foo2)
(define-symbol-macro foo-car (car foo2))
dansalmo
la source
13

On peut penser SETet SETQêtre des constructions de bas niveau.

  • SET peut définir la valeur des symboles.

  • SETQ peut définir la valeur des variables.

Ensuite, il SETFy a une macro, qui fournit de nombreux types de paramètres: symboles, variables, éléments de tableau, emplacements d'instance, ...

Pour les symboles et les variables, on peut penser comme si se SETFdéveloppait en SETet SETQ.

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Donc SETet SETQsont utilisés pour implémenter certaines des fonctionnalités de SETF, qui est la construction la plus générale. Certaines des autres réponses vous racontent une histoire un peu plus complexe, lorsque nous prenons en compte les macros de symboles.

Rainer Joswig
la source
4

Je voudrais ajouter aux réponses précédentes que setf est une macro qui appelle une fonction spécifique en fonction de ce qui a été passé comme premier argument. Comparez les résultats de l'extension macro de setf avec différents types d'arguments:

(macroexpand '(setf a 1))

(macroexpand '(setf (car (list 3 2 1)) 1))

(macroexpand '(setf (aref #(3 2 1) 0) 1))

Pour certains types d'arguments, la "fonction setf" sera appelée:

(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))
Filipp
la source