¿Cuándo citar una expresión lambda?

30

P: ¿ Cuándo, si alguna vez, es útil citar con precisión a lambda, y cuándo, si alguna vez, no debemos citar con precisión a lambda?

Las personas usan lambdas de tres maneras:

  1. llanura: (lambda (x) x)
  2. citado: '(lambda (x) x)
  3. comillas afiladas: #'(lambda (x) x)

Este subproceso SO analiza los tres tipos, este subproceso SO explica por qué no se debe citar (NB: no una comilla nítida ) lambda, y este subproceso SO también discute las distinciones entre comillas y comillas nítidas.

Ahora, el nodo manual sobre funciones anónimas y la cadena de documentación para lambdatener en cuenta que los lambdas son autocomillas

Una llamada de la forma (lambda ARGS DOCSTRING INTERACTIVE BODY)es auto citando; El resultado de evaluar la expresión lambda es la expresión misma. La expresión lambda se puede tratar como una función ...

Entonces, parece que (lambda (x) x)y #'(lambda (x) x)son equivalentes, pero '(lambda (x) x)no lo es (lo más importante, cuando se compila byte).

Parece que uno raramente querría citar un lambda, pero no me queda claro cuándo, si alguna vez, deberíamos, o no, citar con nitidez:

  • ¿Las comillas afiladas son lambdasimplemente una elección estilística, o hay circunstancias en las que las comillas afiladas son realmente útiles?
  • ¿Hay circunstancias en las que tenemos que no afilada citar una lambda, es decir, cuando se hace de manera alteraría el significado del código?
Dan
fuente

Respuestas:

28

Érase una vez , la cita aguda era necesaria para lambdas, ahora ese ya no es el caso.

Entonces, parece que (lambda (x) x) y # '(lambda (x) x) son equivalentes, pero' (lambda (x) x) no lo es (lo más importante, cuando se compila byte).

Sí. De hecho, los dos primeros son completamente idénticos cuando se evalúan. Como se describe en la página del manual que ha vinculado:

Las siguientes formas son todas equivalentes:

(lambda (x) (* x x)) 
(function (lambda (x) (* x x))) 
#'(lambda (x) (* x x))

Aparte de tratar de admitir versiones de Emacs de hace dos décadas, nunca hay una razón para citar una lambda.

Entonces no lo hagas.


Como nota al margen:

  • Citar duro un lambda (con ') hace la diferencia, evita la compilación de bytes. No puedo pensar en un escenario en el que sea útil, pero quién sabe.

  • El backtic es la única cita que es realmente útil con lambdas, pero solo si no está utilizando el enlace léxico por alguna razón.

Malabarba
fuente
Considere agregar un enlace a la sección Funciones anónimas del manual que contiene un ejemplo que explica el efecto de las citas en la compilación de bytes.
Constantine
@Constantine Hecho. Me volví flojo porque estoy en un teléfono, y el OP ya lo conectó de todos modos.
Malabarba
¿Puedes aclarar a qué te refieres con no usar el enlace léxico y el backtick? Gracias.
coredump
@coredump Con el enlace dinámico, la única forma de hacer que las variables externas sean accesibles dentro de una lambda es construir manualmente la lambda como una lista con la variable dentro. Los backtics son buenos para ese tipo de cosas.
Malabarba
Por cierto, no creo que se aplique realmente "alguna vez": cuando investigué este tema en el historial de revisiones, descubrí que lambdase ha definido como una macro que agrega lo functionmás atrás posible. IOW si #'se ha necesitado en algún momento, estaba en un código de desarrollo muy temprano. Seguro que no era necesario ya en Emacs-18.
Stefan
5

Dado lambdaque no tiene ningún sentido cuando no se cita, las versiones recientes de Emacs Lisp siguen (ANSI) Common Lisp para interpretar sin comillas (lambda...)como #'(lambda...). Las dos notaciones son casi exactamente equivalentes (excepto dentro de la estructura citada).

Ya sea que prefiera (lambda...)o #'(lambda...)sea, por lo tanto, una cuestión de estilo. Algunas personas prefieren la forma desnuda, que evita el ruido sintáctico, mientras que otras (incluido yo mismo) prefieren la forma citada.

jch
fuente
Esto contradice el manual de elisp: "En Emacs Lisp, dicha lista es una expresión válida que se evalúa como un objeto de función".
djechlin
8
"Versiones recientes" como en "versiones lanzadas después de 1990 más o menos" ;-)
Stefan
0

Agregando un poco de historia adicional, a causa de ver ¿Es el legado histórico # '(lambda ...)?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=4290 sugiere que:

Comenzando con Emacs 22, un lambdaformulario se compila en bytes cuando se usa como una función, independientemente de si está precedido por functiono #'. Con las versiones de Emacs anteriores a la 22, debe usar explícitamente #' o functionsi desea que el formulario se compile en bytes.

No sé sobre el compilador de bytes, pero puedo ver que, al menos desde 1993, la lambdamacro misma devolvió un (function (lambda ...))formulario.

https://www.iro.umontreal.ca/~monnier/hopl-4-emacs-lisp.pdf también dice:

Curiosamente (a diferencia de MacLisp), lambdatécnicamente no era parte del lenguaje Elisp hasta alrededor de 1991 cuando se agregó como macro, temprano durante el desarrollo de Emacs-19. En Emacs-18, las funciones anónimas se escribieron como valores citados de la forma:

'(lambda (..ARGS..) ..BODY..)

Si bien la lambdamacro ha hecho que esta cita sea innecesaria durante casi 30 años, muchas instancias de esta práctica todavía ocurren en el código de Elisp, a pesar de que evita la compilación de bytes del cuerpo. Algo relacionado, solo en 1993 Lucid Emacs 19.8 importó la #'...taquigrafía del lector (function ...)de MacLisp. Emacs hizo lo mismo el año siguiente.

phils
fuente
0

Solo quiero dar un ejemplo práctico del uso de la expresión lambda backtic. Se trata del enlace léxico / sombreado de variables, el uso de una expresión lambda backtic y la referencia de variables con una coma hace posible obtener su valor global.

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall `(lambda()
             (let ((my-variable "Random"))
               (message ,my-variable)))))

M-x [RET] eval-buffer salidas "Aleatorio antiguo"

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall (lambda()
             (let ((my-variable "Random"))
               (message my-variable)))))

M-x [RET] eval-buffer salidas "Aleatorio"

Un tercer ejemplo que combina la variable global y la variable local de variable

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall `(lambda()
              (let ((my-variable "Random"))
                (message my-variable)
                (message ,my-variable)))))

M-x [RET] eval-buffer salidas "Aleatorio" "Aleatorio antiguo"

cjohansson
fuente
@npostavs ese no era el punto con mi ejemplo, pero también modifiqué mi ejemplo para evitar esa mala práctica
cjohansson
Mejor, aunque todavía no estoy convencido de que esto sea una mejora con solo elegir un nombre diferente para el enlace interno.
npostavs