Différence entre `set`, `setq", et "setf" en Common Lisp?
Quelle est la différence entre "set"," setq "et" setf " dans Common Lisp?
6 réponses
À L'origine, en Lisp, il n'y avait pas de variables lexicales-seulement des variables dynamiques. Et il n'y avait pas de SETQ ou de SETF, juste la fonction SET.
Ce qui est maintenant écrit comme:
(setf (symbol-value '*foo*) 42)
A Été écrit comme:
(set (quote *foo*) 42)
Qui a finalement été abrégé en SETQ (Set Quoted):
(setq *foo* 42)
Ensuite, des variables lexicales se sont produites, et SETQ est venu à être utilisé pour leur assigner aussi - donc ce n'était plus un simple wrapper autour de SET.
Plus tard, quelqu'un a inventé SETF (SET Field) comme manière 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.
Puis les 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 le "variable" assignée à était vraiment une macro de symbole:
(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))
foo => 42
(setq foo 13)
foo => 13
*hidden* => (13 . 42)
Nous arrivons donc à nos jours: SET et SETQ sont des restes atrophiés de dialectes plus anciens, et seront probablement démarrés à partir d'éventuels successeurs de Common Lisp.
(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
est juste comme set
avec une cité en premier arg -- (set 'foo '(bar baz))
est comme (setq foo '(bar baz))
. setf
, d'un autre côté, est subtil en effet - c'est comme une "indirection". Je suggère http://www.n-a-n-o.com/lisp/cmucl-tutorials/LISP-tutorial-16.html comme une meilleure façon de commencer à le comprendre que n'importe quelle réponse ici peut donner... bref, même si, setf
, prend le premier argument comme une "référence", de sorte que, par exemple, (aref myarray 3)
travailler (comme le premier arg à setf
) pour définir un élément dans un tableau.
Vous pouvez utiliser setf
à la place de set
ou setq
mais pas l'inverse puisque setf
peut également définir la valeur des éléments individuels d'une variable si la variable a des éléments individuels. Voir les exaples 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 symbole 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 que vous ne voulez pas lui donner de valeur avant plus tard dans votre code.
(defvar foo2)
(define-symbol-macro foo-car (car foo2))
On peut penser que SET
et SETQ
sont des constructions de bas niveau.
SET
peut définir la valeur des symboles.SETQ
pouvez définir la valeur des variables.
Puis SETF
est 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 que si SETF
s'étend dans 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 mettre en œuvre certaines des fonctionnalités de SETF
, qui est la construction plus générale. Certaines des autres réponses vous racontent l'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. Comparer les résultats de l'expansion 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 "setf fonction" sera appelé:
(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))