¿Cómo saber cuándo o cuándo no usar la comilla simple antes de los nombres de variables?

31

Tengo lo siguiente:

(setq some-variable "less")

Estoy confundido por qué tengo que usar la comilla simple con boundppero no con bound-and-true-p.

Ejemplo 1:

(when (boundp 'some-variable) 
   (message "some-variable is %s" some-variable))

Resultado:

"alguna variable es menos"

Ejemplo 2a:

(when (bound-and-true-p some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))

Resultado:

"alguna variable es menos"

Ejemplo 2b:

(when (bound-and-true-p 'some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))

Resultado:

y: Argumento de tipo incorrecto: symbolp, (entre comillas)

Kaushal Modi
fuente
55
Vale la pena mencionar que setqsignifica set quoted, y originalmente era una macro que se expandió en (set 'some-variable "less"). En general, Elisp no es terriblemente coherente con los argumentos entre comillas y sin comillas, pero cualquier función (no macro) que necesite interactuar con una variable en lugar de un valor tomará su argumento entre comillas ( setqsiendo una excepción importante).
shosti
2
FWIW, bound-and-true-pes una estúpida macro. O más bien, su nombre es estúpido. El 99,99% de las veces que quieres hacerlo (and (boundp 'FOO) FOO)lo haces para usar el valor deFOO . No lo haces solo para obtener un valor de verdad. (1) La macro no es necesaria: el código que reemplaza es trivial y pequeño. (2) El nombre es engañoso: se trata del valor de la variable, no solo de probar si el valor de la variable es o no nil.
Dibujó

Respuestas:

24

Respuesta corta

Si está tratando de usar la variable en sí, entonces use 'some-variable. Si está intentando usar el valor almacenado en la variable, use some-variable.

  • boundp usa un símbolo para que vea cualquier cosa que se pueda vincular, incluidas las funciones. Solo le importa si hay un símbolo que coincida, no cuál es el valor.
  • bound-and-truep usa var y devuelve el valor. En este caso, debe proporcionar el valor del símbolo a la función. Si no se vincula ningún símbolo var, o el valor es nulo, devolverá nil.

Explicación

Para la definición del manual, consulte el manual .

'y (quote ...)ambos realizan el mismo propósito en emacs-lisp.

El propósito de esto es pasar la forma no evaluada al entorno circundante en lugar de evaluarla.

En su ejemplo, suponga que tenemos el siguiente más arriba

(setq some-variable "less") ;; Rather than just 't for clarity

Luego la evaluación es la siguiente:

(when (boundp 'some-variable) 
   (message "some-variable is %s" some-variable))
;; ==> (boundp 'some-variable) ; 't
;; ==> some-variable is "less"

Mientras que sin una cita:

(when (boundp some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))
;; ==> (boundp "less") ; "less" is not a variable. -> Error

Lisp evalúa los formularios a medida que se alcanzan, citando el formulario que evita la evaluación para que se pase la variable real (o la lista, o el nombre de la función).

Jonathan Leech-Pepin
fuente
¡Gracias! Su respuesta me hizo saltar a las fuentes de ambos y me ayudó a encontrar la solución. También actualicé mi pregunta con ejemplos claros.
Kaushal Modi
66
Me alegra que esto haya ayudado a la operación, pero en realidad no responde la pregunta (de alguna manera útil para otras personas que tienen la misma pregunta). Solo explica que los símbolos tienen que ser citados o de lo contrario se evalúan. No explica por qué ese no es el caso bound-and-truepy la pregunta era por qué tengo que citar cuando lo uso boundppero no cuando lo uso bound-and-truep.
tarsius
@tarsius En realidad incluyo la distinción entre boundprequerir un símbolo (sin evaluar) y bound-and-truepnecesitar el valor de la variable en los puntos (editado después de la respuesta inicial)
Jonathan Leech-Pepin
Creo que es esencial mencionar por qué es así (las macros pueden optar por no evaluar) en lugar de mencionar que la cadena de documentación lo dice. Creo que esta es una muy buena pregunta y que boundp vs. bound-and-true-p es solo un ejemplo. Lo que realmente se reduce a la pregunta es el deseo de aprender sobre las reglas de evaluación.
tarsius
@tarsius ¿No se explican las reglas de evaluación en esta respuesta? Al menos los básicos que solo incluyen la cita ... Su respuesta es definitivamente más completa, pero va mucho más allá del tema, ¿no?
T. Verron
17

Un símbolo que se encuentra en una posición sin función se trata como el nombre de una variable. In (function variable) functionestá en posición de función (después del paréntesis de apertura) y variableno lo está. A menos que las variables citadas explícitamente se reemplacen con sus valores.

Si (boundp my-variable)tuviera que escribir eso significaría "es el símbolo que se almacena en el valor de la variable my-variablevinculada como una variable" y no "es el símbolo my-variablevinculado como una variable.

Entonces, ¿por qué se bound-and-truepcomporta de manera diferente?

Esta es una macro y las reglas normales de evaluación (función) no se aplican aquí, las macros son libres de decidir si se evalúan sus argumentos y cuándo. Lo que las macros realmente hacen es de alguna manera transformar los argumentos y devolver el resultado como una lista, que luego se evalúa. La transformación y la evaluación final ocurren en diferentes momentos, llamados tiempo de macroexpansión y tiempo de evaluación.

Así es bound-and-true-pcomo se ve la definición de :

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  `(and (boundp (quote ,var)) ,var))

Esto usa macros de lector que son diferentes de las macros de lisp (más sobre eso a continuación). Para no complicar esto aún más, no usemos ninguna macro de lector:

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  (list 'and (list 'boundp (list 'quote var)) var))

Si tú escribes

(bound-and-true-p my-variable)

que primero se "traduce" a

(and (boundp 'my-variable) my-variable)

y luego se evalúa devolviendo nilsi my-variableno es boundpo de lo contrario el valor de my-variable(que por supuesto también puede ser nil).


Es posible que hayas notado que la expansión no fue

(and (boundp (quote my-variable)) my-variable)

como podríamos haber esperado quotees una forma especial, no una macro o función. Al igual que las macros, las formas especiales pueden hacer lo que sea con sus argumentos. Esta forma especial en particular simplemente devuelve su argumento, aquí un símbolo, en lugar del valor variable del símbolo. Ese es en realidad el único propósito de esta forma especial: ¡evitar la evaluación! Las macros no pueden hacer eso por sí mismas, necesitan usarlas quotepara hacerlo.

Entonces, ¿qué pasa '? Es una macro lectora , que como se mencionó anteriormente no es lo mismo que una macro lisp . Mientras que las macros se usan para transformar código / datos, las macros de lector se usan antes cuando se lee texto para transformar ese texto en código / datos.

'something

es una forma corta de

(quote something)

`utilizado en la definición real de bound-and-true-ptambién es una macro lectora. Si cita un símbolo como en `symboles equivalente a 'symbol, pero cuando se usa para citar una lista como en, `(foo bar ,baz)se comporta de manera diferente en la medida en que ,se evalúan los formularios con prefijo .

`(constant ,variable)

es equivalente a

(list (quote constant) variable))

Esto debería responder a la pregunta de por qué los símbolos sin comillas a veces se evalúan (reemplazan con sus valores) y otras no; Las macros pueden usarse quotepara evitar que un símbolo sea evaluado.

¿Pero por qué es bound-and-true-puna macro mientras boundpque no lo es? Tenemos que poder determinar si los símbolos arbitrarios, que no se conocen hasta el tiempo de ejecución, están vinculados como símbolos. Esto no sería posible si el boundpargumento se citara automáticamente.

bound-and-true-pse usa para determinar si se define una variable conocida y, en caso afirmativo, usar su valor. Esto es útil si una biblioteca tiene una dependencia opcional en una biblioteca de terceros como en:

(defun foo-get-value ()
  (or (bound-and-true-p bar-value)
      ;; we have to calculate the value ourselves
      (our own inefficient or otherwise undesirable variant)))

bound-and-true-ppodría definirse como una función y requerir que se cite el argumento, pero porque está destinado a casos en los que sabe por adelantado qué variable le importa una macro se usó para evitar que tenga que escribir el '.

tarsius
fuente
Sorprendentemente bien respondido.
Charles Ritchie
2

Del código fuente de boundp:

DEFUN ("boundp", Fboundp, Sboundp, 1, 1, 0,
      doc: /* Return t if SYMBOL's value is not void.
Note that if `lexical-binding' is in effect, this refers to the
global value outside of any lexical scope.  */)

boundpespera a symbolcomo entrada. 'some-variableEs un símbolo de la variable some-variable.

Del código fuente de bound-and-true-p:

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  `(and (boundp (quote ,var)) ,var))

bound-and-true-pespera a variablecomo entrada.

Dentro de la bound-and-true-pmacro, obtiene el símbolo haciendo (quote ,var). Entonces, si la entrada es some-variable, (quote ,var)dará como resultado 'some-variable.

Pero cuando doy la entrada 'some-variablea bound-and-true-p, obtengo el error: and: Wrong type argument: symbolp, (quote some-variable)porque la macro NO espera un símbolo ( 'some-variable) en la entrada.

Kaushal Modi
fuente
Solo un aviso. ''symbol Tiene sentido. Significa (quote (quote symbol)). La razón por la que obtiene un error es que no es un argumento válido para boundp.
Malabarba
@Malabarba Gracias. He hecho la corrección.
Kaushal Modi