¿Diferencia entre `set`,` setq` y `setf` en Common Lisp?

166

¿Cuál es la diferencia entre "set", "setq" y "setf" en Common Lisp?

Richard Hoskins
fuente
9
El comportamiento de estos se responde bastante bien en las respuestas, pero la respuesta aceptada tiene una etimología posiblemente equivocada para la "f" en "setf". La respuesta a ¿Qué significa la f en setf? dice que es para "función" y proporciona referencias para respaldarlo.
Joshua Taylor

Respuestas:

169

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.

programador de pila
fuente
45
Lisp común siempre tuvo variables léxicas. Debes estar hablando de algunos Lisp antes de Common Lisp.
Rainer Joswig
44
Si SET y SETQ se inician desde un sucesor de Common Lisp, tendrán que ser reemplazados. Su uso en el código de alto nivel es limitado, pero el código de bajo nivel (por ejemplo, el código SETF está implementado en) los necesita.
Svante
13
¿Hay alguna razón por la que eligió 'automóvil' como campo en lugar de algo que podría confundirse como la función del automóvil?
drudru
9
Esta respuesta a ¿Qué significa la f en setf? afirma que frealmente 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.
Joshua Taylor
1
Resumen: setes una función. Por lo tanto, no conoce el medio ambiente. setNo se puede ver la variable léxica. Solo puede establecer el valor de símbolo de su argumento. setqya no se "establece entre comillas". El hecho de que setqsea ​​una forma especial, no una macro, lo demuestra.
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
fuente
12
Creo que su respuesta es más clara que la más votada. Muchas gracias.
CDR
2
@Sourav, NUNCA use la letra "l" (ell) como variable o símbolo en el código de ejemplo. Es demasiado difícil distinguir visualmente del número 1.
DavidBooth
No, todavía no entiendo cómo (car ls) puede ser un valor l o no. ¿Entiendes cómo traducir CLisp a C? y como escribir un intérprete CLisp?
reúne el
@ user1952009 clisp es una implementación de Common Lisp. Si desea hacer referencia al lenguaje en sí, CL es la abreviatura más utilizada.
ssice
2
@ user1952009 Después (setq ls '(((1)))), (setf (car (car (car ls))) 5)es un comportamiento indefinido, porque el valor de lses 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 = 5en C.
kyle
21

setqes igual que setcon 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, setftoma 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.

Alex Martelli
fuente
1
Tiene más sentido para el nombre setq. Fácil de recordar. Gracias.
CDR
17

Puede usarlo setfen lugar de seto setqno viceversa, ya setfque 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)

setftiene la capacidad adicional de establecer un miembro de la lista en fooun 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 defvarsi 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))
dansalmo
fuente
13

Uno puede pensar SETy SETQser construcciones de bajo nivel.

  • SET Puede establecer el valor de los símbolos.

  • SETQ Puede establecer el valor de las variables.

Luego SETFhay 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 SETFexpandiera en SETy SETQ.

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Entonces, SETy SETQse 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.

Rainer Joswig
fuente
4

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))
Filipp
fuente