web-dev-qa-db-fra.com

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

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

155
Richard Hoskins

À l'origine, dans LISP, il n'y avait pas de variables lexicales - seulement des variables dynamiques. Et il n'y avait pas de SETQ ou 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 a également été utilisé pour leur assignation - 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'affecter des valeurs à des structures de données, afin de refléter les valeurs l d'autres langues:

x.car := 42;

serait écrit comme

(setf (car x) 42)

Pour des raisons de symétrie et de généralité, SETF a également fourni les fonctionnalités 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, on s'est rendu compte que SETQ devrait se comporter comme SETF si la "variable" affectée était réellement 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 de nos jours: SET et SETQ sont des restes atrophiés d'anciens dialectes et seront probablement initialisés par d'éventuels successeurs de Common LISP.

161
stack programmer
(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
130
Sourav

setq est comme set avec un premier argument cité - (set 'foo '(bar baz)) est comme (setq foo '(bar baz)). setf, en revanche, est subtil - c'est comme un "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 proposée ici ne peut donner ... en bref, cependant, setf prend le premier argument comme "référence", de sorte que, par exemple, (aref myarray 3) fonctionnera (en tant que premier argument de setf) pour définir un élément dans un tableau.

21
Alex Martelli

Vous pouvez utiliser setf à la place de set ou setq mais pas l'inverse car setf peut é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 assigneront 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 possibilité supplémentaire de définir un membre de la liste dans foo sur 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 lui attribuer une valeur que plus tard dans votre code.

(defvar foo2)
(define-symbol-macro foo-car (car foo2))
16
dansalmo

On peut penser que SET et SETQ sont des constructions de bas niveau.

  • SET peut définir la valeur des symboles.

  • SETQ peut définir la valeur des variables.

Ensuite, 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 se développe en SET et SETQ.

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Ainsi, 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 tenons compte des macros de symboles.

12
Rainer Joswig

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'expansion de la 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, "fonction setf" sera appelée:

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