¿Cuál es la diferencia entre "set", "setq" y "setf" en Common Lisp?
¿Cuál es la diferencia entre "set", "setq" y "setf" en Common Lisp?
Respuestas:
Originalmente, en Lisp, no había variables léxicas, solo dinámicas. Y no había SETQ o SETF, solo la función SET.
Lo que ahora se escribe como:
(setf (symbol-value '*foo*) 42)
fue escrito como:
(set (quote *foo*) 42)
que finalmente se abrevió a SETQ (SET Citado):
(setq *foo* 42)
Luego sucedieron las variables léxicas, y SETQ también se usó para asignarles, por lo que ya no era un simple envoltorio alrededor de SET.
Más tarde, alguien inventó SETF (campo SET) como una forma genérica de asignar valores a estructuras de datos, para reflejar los valores l de otros lenguajes:
x.car := 42;
se escribiría como
(setf (car x) 42)
Por simetría y generalidad, SETF también proporcionó la funcionalidad de SETQ. En este punto, habría sido correcto decir que SETQ era una primitiva de bajo nivel y SETF una operación de alto nivel.
Entonces sucedieron las macros de símbolos. Para que las macros de símbolos pudieran funcionar de forma transparente, se dio cuenta de que SETQ tendría que actuar como SETF si la "variable" a la que se asignaba era realmente una macro de símbolos:
(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))
foo => 42
(setq foo 13)
foo => 13
*hidden* => (13 . 42)
Así que llegamos en la actualidad: SET y SETQ son restos atrofiados de dialectos más antiguos, y probablemente serán arrancados de los sucesores sucesivos de Common Lisp.
f
realmente representa la función , no el campo (o la forma , para el caso), y proporciona referencias, por lo que si bien el setf para el campo tiene algún sentido, parece que podría no ser correcto.
set
es una función. Por lo tanto, no conoce el medio ambiente. set
No se puede ver la variable léxica. Solo puede establecer el valor de símbolo de su argumento. setq
ya no se "establece entre comillas". El hecho de que setq
sea una forma especial, no una macro, lo demuestra.
(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)
es un comportamiento indefinido, porque el valor de ls
es constante (como modificar un literal de cadena en C). Después (setq ls (list (list (list 1))))
, (setf (car (car (car ls))) 5)
funciona igual que ls->val->val->val = 5
en C.
setq
es igual que set
con un primer argumento citado, (set 'foo '(bar baz))
es como (setq foo '(bar baz))
. setf
, por otro lado, es sutil de hecho, es como una "indirección". Sugiero http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html como una mejor manera de comenzar a entenderlo que cualquier respuesta aquí que pueda dar ... en resumen, sin embargo, setf
toma el primer argumento como "referencia", de modo que, por ejemplo (aref myarray 3)
, funcionará (como el primer argumento setf
) para establecer un elemento dentro de una matriz.
Puede usarlo setf
en lugar de set
o setq
no viceversa, ya setf
que también puede establecer el valor de elementos individuales de una variable si la variable tiene elementos individuales. Vea los ejemplos a continuación:
Los cuatro ejemplos asignarán la lista (1, 2, 3) a la variable llamada 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
tiene la capacidad adicional de establecer un miembro de la lista en foo
un nuevo valor.
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)
Sin embargo, puede definir una macro de símbolo que represente un solo elemento dentro de 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)
Puede usarlo defvar
si aún no ha definido la variable y no desea darle un valor hasta más adelante en su código.
(defvar foo2)
(define-symbol-macro foo-car (car foo2))
Uno puede pensar SET
y SETQ
ser construcciones de bajo nivel.
SET
Puede establecer el valor de los símbolos.
SETQ
Puede establecer el valor de las variables.
Luego SETF
hay una macro, que proporciona muchos tipos de elementos de configuración: símbolos, variables, elementos de matriz, ranuras de instancia, ...
Para símbolos y variables uno puede pensar como si se SETF
expandiera en SET
y SETQ
.
* (macroexpand '(setf (symbol-value 'a) 10))
(SET 'A 10)
* (macroexpand '(setf a 10))
(SETQ A 10)
Entonces, SET
y SETQ
se utilizan para implementar algunas de las funciones de SETF
, que es la construcción más general. Algunas de las otras respuestas le cuentan la historia un poco más compleja, cuando tomamos en cuenta las macros de símbolos.
Me gustaría agregar a las respuestas anteriores que setf es una macro que llama a una función específica dependiendo de lo que se pasó como primer argumento. Compare los resultados de la expansión macro de setf con diferentes tipos de argumentos:
(macroexpand '(setf a 1))
(macroexpand '(setf (car (list 3 2 1)) 1))
(macroexpand '(setf (aref #(3 2 1) 0) 1))
Para algunos tipos de argumentos, se llamará "función setf":
(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))