Quelle est la différence entre "set", "setq" et "setf" en Common Lisp?
Quelle est la différence entre "set", "setq" et "setf" en Common Lisp?
Réponses:
À 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.
f
repré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.
set
est une fonction. Ainsi, il ne connaît pas l'environnement. set
ne peut pas voir la variable lexicale. Il ne peut définir que la valeur de symbole de son argument. setq
n'est plus "set quoted". Le fait qu'il setq
s'agisse d'une forme spéciale et non d'une macro le montre.
(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)
est un comportement indéfini, car la valeur de ls
est 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 = 5
dans C.
setq
est juste comme set
avec 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, setf
prend 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.
Vous pouvez utiliser setf
à la place set
ou setq
mais pas l'inverse, car vous setf
pouvez é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)
setf
a la capacité supplémentaire de définir un membre de la liste dans foo
une 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 defvar
si 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))
On peut penser SET
et 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 SETF
y 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 SETF
développait en SET
et SETQ
.
* (macroexpand '(setf (symbol-value 'a) 10))
(SET 'A 10)
* (macroexpand '(setf a 10))
(SETQ A 10)
Donc SET
et SETQ
sont 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.
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))