¿Cuándo usar '(o citar) en Lisp?

114

Después de recorrer las partes principales de un libro introductorio de Lisp, todavía no podía entender qué hace la función de operador especial (quote)(o equivalente '), sin embargo, esto ha estado en todo el código Lisp que he visto.

¿Qué hace?

Cristián Romo
fuente

Respuestas:

178

Respuesta corta Omita las reglas de evaluación predeterminadas y no evalúe la expresión (símbolo o s-exp), pasándola a la función exactamente como se escribió.

Respuesta larga: la regla de evaluación predeterminada

Cuando se invoca una función regular (lo abordaré más adelante), se evalúan todos los argumentos que se le pasan. Esto significa que puede escribir esto:

(* (+ a 2)
   3)

Que a su vez evalúa (+ a 2), evaluando ay 2. El valor del símbolo ase busca en el conjunto de vinculación de variables actual y luego se reemplaza. Say aestá actualmente vinculado al valor 3:

(let ((a 3))
  (* (+ a 2)
     3))

Obtendríamos (+ 3 2), luego se invoca + en 3 y 2, lo que da como resultado 5. Nuestra forma original ahora (* 5 3)produce 15.

¡Explica quoteya!

Bien. Como se vio anteriormente, se evalúan todos los argumentos de una función, por lo que si desea pasar el símbolo a y no su valor, no desea evaluarlo. Los símbolos Lisp pueden duplicar sus valores y marcadores en los que en otros lenguajes habría utilizado cadenas, como claves para tablas hash.

Aquí es donde quoteentra en juego. Supongamos que desea trazar las asignaciones de recursos desde una aplicación Python, pero más bien hacer el trazado en Lisp. Haga que su aplicación Python haga algo como esto:

print("'(")
while allocating:
    if random.random() > 0.5:
        print(f"(allocate {random.randint(0, 20)})")
    else:
        print(f"(free {random.randint(0, 20)})")
    ...
print(")")

Dándole una salida con este aspecto (un poco bastante bonito):

'((allocate 3)
  (allocate 7)
  (free 14)
  (allocate 19)
  ...)

¿Recuerda lo que dije sobre quote("marcar") que provocó que la regla predeterminada no se aplicara? Bueno. Lo que de otro modo sucedería es que se busquen los valores de allocatey free, y no queremos eso. En nuestro Lisp, deseamos hacer:

(dolist (entry allocation-log)
  (case (first entry)
    (allocate (plot-allocation (second entry)))
    (free (plot-free (second entry)))))

Para los datos proporcionados anteriormente, se habría realizado la siguiente secuencia de llamadas a funciones:

(plot-allocation 3)
(plot-allocation 7)
(plot-free 14)
(plot-allocation 19)

Pero ¿qué pasa list?

Bueno, a veces tu no desea evaluar los argumentos. Digamos que tiene una función ingeniosa que manipula un número y una cadena y devuelve una lista de las ... cosas resultantes. Hagamos un comienzo en falso:

(defun mess-with (number string)
  '(value-of-number (1+ number) something-with-string (length string)))

Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))

¡Oye! Eso no es lo que queríamos. Queremos evaluar selectivamente algunos argumentos y dejar los demás como símbolos. ¡Prueba el número 2!

(defun mess-with (number string)
  (list 'value-of-number (1+ number) 'something-with-string (length string)))

Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)

No solo quote, sinobackquote

¡Mucho mejor! Por cierto, este patrón es tan común en (en su mayoría) macros, que existe una sintaxis especial para hacer precisamente eso. La cita inversa:

(defun mess-with (number string)
  `(value-of-number ,(1+ number) something-with-string ,(length string)))

Es como usar quote , pero con la opción de evaluar explícitamente algunos argumentos añadiéndoles una coma. El resultado es equivalente a usar list, pero si está generando código a partir de una macro, a menudo solo desea evaluar pequeñas partes del código devuelto, por lo que la cita inversa es más adecuada. Para listas más cortas, listpuede ser más legible.

Oye, te olvidaste de quote !

¿A dónde nos lleva esto? Oh cierto, ¿qué hace quoterealmente? ¡Simplemente devuelve su (s) argumento (s) sin evaluar! ¿Recuerda lo que dije al principio sobre las funciones regulares? Resulta que algunos operadores / funciones necesitan no evalúan sus argumentos. Como SI, no querría que se evaluara la rama else si no se hubiera tomado, ¿verdad? Así llamado operadores especiales , junto con las macros, funcionan así. Los operadores especiales también son el "axioma" del lenguaje, un conjunto mínimo de reglas, sobre el que puede implementar el resto de Lisp combinándolos de diferentes maneras.

Sin quoteembargo, volviendo a:

Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL

Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
SPIFFY-SYMBOL

Comparar con (en Steel-Bank Common Lisp):

Lisp> spiffy-symbol
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING   {A69F6A9}>:
  The variable SPIFFY-SYMBOL is unbound.

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
0] 

Porque no hay spiffy-symbol en el ámbito actual!

Resumiendo

quote, backquote(con coma), y listson algunas de las herramientas que usa para crear listas, que no son solo listas de valores, sino que, como puede ver, pueden usarse como ligeras (no es necesario definir unstruct estructuras de datos ).

Si desea obtener más información, le recomiendo el libro Practical Common Lisp de Peter Seibel para un enfoque práctico del aprendizaje de Lisp, si ya está interesado en la programación. Eventualmente, en su viaje Lisp, también comenzará a usar paquetes. Ron Garret's The Idiot's Guide to Common Lisp Packages le dará una buena explicación de ellos.

¡Feliz piratería!

Mikael Jansson
fuente
En mi emacs, SBCL está configurado y cuando escribo 'esto' es 'verdadero', solo devuelve el último, es decir, VERDADERO en la salida. Incluso en portacle obtengo el mismo resultado
Totoro
@Totoro El valor de retorno de una función o solo varias declaraciones en lisp es la última expresión, por lo que en realidad devuelve this, entonces is, entonces true, pero solo ve la última devuelta. (esto es y la verdad son declaraciones separadas)
Wezl
52

Dice "no me evalúes". Por ejemplo, si quisiera usar una lista como datos y no como código, colocaría una cita delante. Por ejemplo,

(print '(+ 3 4))imprime "(+ 3 4)", mientras que (print (+ 3 4))imprime "7"

Adam Rosenfield
fuente
¿Cómo podría evaluarlo entonces, por ejemplo, hay un unquotecomando?
Lima
3
@William Lisps tienen una función práctica llamada eval: (print (eval '(+ 3 4))). Esto es lo que hace que Lisps sea tan grandioso: las listas son código y el código son listas, por lo que un programa Lisp puede manipularse a sí mismo.
darkfeline
18

Otras personas han respondido admirablemente a esta pregunta y Matthias Benkard presenta una excelente advertencia.

NO USE COTIZACIÓN PARA CREAR LISTAS QUE MODIFICARÁ MÁS TARDE. La especificación permite al compilador tratar las listas entre comillas como constantes. A menudo, un compilador optimizará las constantes creando un valor único para ellas en la memoria y luego haciendo referencia a ese valor único desde todas las ubicaciones donde aparece la constante. En otras palabras, puede tratar la constante como una variable global anónima.

Esto puede causar problemas obvios. Si modifica una constante, es muy posible que modifique otros usos de la misma constante en un código completamente no relacionado. Por ejemplo, puede comparar alguna variable con '(1 1) en alguna función, y en una función completamente diferente, comenzar una lista con' (1 1) y luego agregarle más cosas. Al ejecutar estas funciones, puede encontrar que la primera función ya no coincide con las cosas correctamente, porque ahora está tratando de comparar la variable con '(1 1 2 3 5 8 13), que es lo que devolvió la segunda función. Estas dos funciones no están relacionadas en absoluto, pero se afectan entre sí debido al uso de constantes. Incluso pueden ocurrir efectos negativos más locos, como una iteración de lista perfectamente normal de repente un bucle infinito.

Use comillas cuando necesite una lista constante, como para comparar. Use la lista cuando vaya a modificar el resultado.

Xanthir
fuente
Entonces parece que deberías usar la (list (+ 1 2)) mayor parte del tiempo. Si es así, ¿cómo puede evitar la evaluación de (+ 1 2)dentro de tal ejemplo? ¿Hay un unquotecomando?
Lima
1
¿Quieres el equivalente de '((3)), o el equivalente de '((+ 1 2))? En este último caso, hay que usar más list: (list (list '+ 1 2)). O si quisieras el equivalente de '(+ 1 2), simplemente (list '+ 1 2). Y recuerde, si no está modificando la lista, no dude en usar la cita: no hay nada de malo en '(+ 1 2)si solo está comparando con ella o algo así.
Xanthir
1
¿Le importa hacer referencia a dónde se supone que las listas entre comillas deben tratarse como constantes?
Lima
El HyperSpec clhs.lisp.se/Body/s_quote.htm dice que el comportamiento no está definido si el objeto citado se modifica destructivamente. Se da a entender que esto es para permitir que las impls traten los valores como valores atómicos.
Xanthir
14

Una respuesta a esta pregunta dice que QUOTE “crea estructuras de datos de lista”. Esto no es del todo correcto. QUOTE es más fundamental que esto. De hecho, QUOTE es un operador trivial: su propósito es prevenir suceda nada. En particular, no crea nada.

Lo que (CITA X) dice es básicamente "no hagas nada, solo dame X". No es necesario que X sea una lista como en (QUOTE (ABC)) o un símbolo como en (QUOTE FOO). Puede ser cualquier objeto. De hecho, el resultado de evaluar la lista producida por (LIST 'QUOTE SOME-OBJECT) siempre devolverá SOME-OBJECT, cualquiera que sea.

Ahora, la razón por la que (QUOTE (ABC)) parece que creó una lista cuyos elementos son A, B y C es que esa lista es realmente lo que devuelve; pero en el momento en que se evalúa el formulario de COTIZACIÓN, la lista generalmente ya existe desde hace algún tiempo (¡como un componente del formulario de COTIZACIÓN!), creada por el cargador o el lector antes de la ejecución del código.

Una implicación de esto que tiende a confundir a los novatos con bastante frecuencia es que no es prudente modificar una lista devuelta por un formulario de COTIZACIÓN. Los datos devueltos por QUOTE deben considerarse, a todos los efectos, como parte del código que se está ejecutando y, por lo tanto, deben tratarse como de solo lectura.

Matthias Benkard
fuente
11

La cita impide la ejecución o evaluación de un formulario, convirtiéndolo en su lugar en datos. En general, puede ejecutar los datos evaluándolos.

quote crea estructuras de datos de lista, por ejemplo, las siguientes son equivalentes:

(quote a)
'a

También se puede utilizar para crear listas (o árboles):

(quote (1 2 3))
'(1 2 3)

Probablemente sea mejor que obtenga un libro introductorio sobre lisp, como Practical Common Lisp (que está disponible para leer en línea).

Kyle Burton
fuente
3

En Emacs Lisp:

¿Qué se puede cotizar?

Listas y símbolos.

Citar un número se evalúa como el número en sí mismo: '5es lo mismo que 5.

¿Qué pasa cuando cotizas listas?

Por ejemplo:

'(one two) evalúa a

(list 'one 'two) que evalúa a

(list (intern "one") (intern ("two"))).

(intern "one")crea un símbolo llamado "uno" y lo almacena en un mapa hash "central", por lo que cada vez que lo diga 'one, el símbolo nombrado se "one"buscará en ese mapa hash central.

Pero, ¿qué es un símbolo?

Por ejemplo, en lenguajes OO (Java / Javascript / Python) un símbolo podría representarse como un objeto que tiene un namecampo, que es el nombre del símbolo como "one"arriba, y los datos y / o el código pueden asociarse con este objeto.

Entonces, un símbolo en Python podría implementarse como:

class Symbol:
   def __init__(self,name,code,value):
       self.name=name
       self.code=code
       self.value=value

En Emacs Lisp, por ejemplo, un símbolo puede tener 1) datos asociados con él Y (al mismo tiempo, para el mismo símbolo) 2) código asociado, dependiendo del contexto, se llama a los datos o al código.

Por ejemplo, en Elisp:

(progn
  (fset 'add '+ )
  (set 'add 2)
  (add add add)
)

evalúa a 4.

Porque se (add add add)evalúa como:

(add add add)
(+ add add)
(+ 2 add)
(+ 2 2)
4

Entonces, por ejemplo, usando la Symbolclase que definimos en Python anteriormente, este addELisp-Symbol podría escribirse en Python como Symbol("add",(lambda x,y: x+y),2).

Muchas gracias a la gente de IRC #emacs por explicarme los símbolos y las citas.

jhegedus
fuente
2

Cuando queremos pasar un argumento en sí mismo en lugar de pasar el valor del argumento, usamos comillas. Se relaciona principalmente con el procedimiento que pasa durante el uso de listas, pares y átomos que no están disponibles en el lenguaje de programación C (la mayoría de las personas comienzan a programar usando la programación C, por lo que nos confundimos) Este es el código en el lenguaje de programación Scheme, que es un dialecto de lisp y supongo que puedes entender este código.

(define atom?              ; defining a procedure atom?
  (lambda (x)              ; which as one argument x
(and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
(atom? '(a b c)) ; since it is a list it is false #f

La última línea (¿átomo? 'Abc) está pasando abc al procedimiento para comprobar si abc es un átomo o no, pero cuando pasa (¿átomo? Abc), comprueba el valor de abc y pasa el valor a eso. Desde entonces, no le hemos proporcionado ningún valor.

error desconocido
fuente
1

Quote devuelve la representación interna de sus argumentos. Después de analizar demasiadas explicaciones de lo que la cita no sirve , fue entonces cuando se encendió la bombilla. Si el REPL no convirtió los nombres de las funciones a MAYÚSCULAS cuando los cité, es posible que no me haya dado cuenta.

Entonces. Las funciones Lisp ordinarias convierten sus argumentos en una representación interna, evalúan los argumentos y aplican la función. Quote convierte sus argumentos en una representación interna y simplemente lo devuelve. Técnicamente es correcto decir que la cita dice "no evalúes", pero cuando estaba tratando de entender lo que hacía, decirme lo que no hacía era frustrante. Mi tostadora tampoco evalúa las funciones Lisp; pero no es así como explicas lo que hace una tostadora.

Steve
fuente
1

Otra respuesta corta:

quotesignifica sin evaluarlo, y la cita inversa es una cita pero deja puertas traseras .

Una buena referencia:

El manual de referencia de Emacs Lisp lo deja muy claro

9.3 Cotización

La forma especial quote devuelve su único argumento, tal como está escrito, sin evaluarlo. Esto proporciona una forma de incluir símbolos y listas constantes, que no son objetos de autoevaluación, en un programa. (No es necesario citar objetos de autoevaluación como números, cadenas y vectores).

Forma especial: objeto de cotización

This special form returns object, without evaluating it. 

Debido a que la cita se usa con tanta frecuencia en los programas, Lisp proporciona una sintaxis de lectura conveniente para ella. Un carácter de apóstrofo ('' ') seguido de un objeto Lisp (en sintaxis de lectura) se expande a una lista cuyo primer elemento es comillas y cuyo segundo elemento es el objeto. Por lo tanto, la sintaxis de lectura 'x es una abreviatura de (entre comillas x).

A continuación, se muestran algunos ejemplos de expresiones que usan comillas:

(quote (+ 1 2))
      (+ 1 2)

(quote foo)
      foo

'foo
      foo

''foo
      (quote foo)

'(quote foo)
      (quote foo)

9.4 Cita previa

Las construcciones de comillas inversas le permiten citar una lista, pero evaluar selectivamente elementos de esa lista. En el caso más simple, es idéntico a la cotización en forma especial (descrita en la sección anterior; ver Cotización). Por ejemplo, estas dos formas producen resultados idénticos:

`(a list of (+ 2 3) elements)
      (a list of (+ 2 3) elements)

'(a list of (+ 2 3) elements)
      (a list of (+ 2 3) elements)

El marcador especial ',' dentro del argumento de la comilla inversa indica un valor que no es constante. El evaluador de Emacs Lisp evalúa el argumento de ',' y coloca el valor en la estructura de la lista:

`(a list of ,(+ 2 3) elements)
      (a list of 5 elements)

También se permite la sustitución por ',' en niveles más profundos de la estructura de la lista. Por ejemplo:

`(1 2 (3 ,(+ 4 5)))
      (1 2 (3 9))

También puede empalmar un valor evaluado en la lista resultante, usando el marcador especial ', @'. Los elementos de la lista empalmada se convierten en elementos al mismo nivel que los demás elementos de la lista resultante. El código equivalente sin usar '`' a menudo es ilegible. Aquí hay unos ejemplos:

(setq some-list '(2 3))
      (2 3)

(cons 1 (append some-list '(4) some-list))
      (1 2 3 4 2 3)

`(1 ,@some-list 4 ,@some-list)
      (1 2 3 4 2 3)
Andrew_1510
fuente
1
Code is data and data is code.  There is no clear distinction between them.

Esta es una declaración clásica que cualquier programador de lisp conoce.

Cuando cita un código, ese código serán datos.

1 ]=> '(+ 2 3 4)
;Value: (+ 2 3 4)

1 ]=> (+ 2 3 4)
;Value: 9

Cuando cita un código, el resultado serán datos que representan ese código. Entonces, cuando desee trabajar con datos que representan un programa, cite ese programa. Esto también es válido para expresiones atómicas, no solo para listas:

1 ]=> 'code
;Value: code

1 ]=> '10
;Value: 10

1 ]=> '"ok"
;Value: "ok"

1 ]=> code
;Unbound variable: code

Supongamos que desea crear un lenguaje de programación incrustado en lisp; trabajará con programas que se citan en esquema (como '(+ 2 3)) y que se interpretan como código en el lenguaje que crea, dando a los programas una interpretación semántica. En este caso, es necesario utilizar comillas para mantener los datos, de lo contrario, se evaluarán en un idioma externo.

alinsoar
fuente