Quando citare bruscamente un'espressione lambda?


30

D: Quando, se mai, è utile virgolette a lambdae quando, se mai, non dobbiamo virgolette a lambda?

Le persone usano lambdas in tre modi:

  1. plain: (lambda (x) x)
  2. citato: '(lambda (x) x)
  3. sharp-citato: #'(lambda (x) x)

Questo thread SO discute i tre tipi, questo thread SO spiega perché non citare (NB: non virgolette ) lambdae questo thread SO discute anche le distinzioni tra virgolette e virgolette .

Ora, il nodo manuale su funzioni anonime e il docstring per lambdanotare che lambdasono auto-quotanti:

Una chiamata del modulo (lambda ARGS DOCSTRING INTERACTIVE BODY)è auto-quotata; il risultato della valutazione dell'espressione lambda è l'espressione stessa. L'espressione lambda può quindi essere trattata come una funzione ...

Quindi, sembra che (lambda (x) x)e #'(lambda (x) x)siano equivalenti, ma '(lambda (x) x)non lo è (soprattutto, durante la compilazione di byte).

Sembra che uno vorrebbe raramente citare un lambda, ma non mi è chiaro quando, se mai, dovremmo, o non dovremmo, virgolette:

  • La citazione nitida è lambdasemplicemente una scelta stilistica o ci sono circostanze in cui la quotazione forte è effettivamente utile?
  • Ci sono circostanze in cui non dobbiamo fare virgolette acide a lambda, ovvero, quando ciò cambierebbe il significato del codice?

Risposte:


28

C'era una volta la citazione tagliente per lambda, ora non è più così.

Quindi, sembra che (lambda (x) x) e # '(lambda (x) x) siano equivalenti, ma' (lambda (x) x) non lo è (soprattutto, durante la compilazione di byte).

Sì. In effetti, i primi due sono completamente identici quando valutati. Come descritto nella pagina del manuale che hai collegato:

I seguenti moduli sono tutti equivalenti:

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

Oltre a provare a supportare le versioni di Emacs di due decenni fa, non c'è mai motivo di citare in modo netto un lambda.

Quindi non farlo.


Come sidenote:

  • La quotazione forzata di una lambda (con ') fa la differenza, impedisce la compilazione di byte. Non riesco a pensare a uno scenario in cui sia utile, ma chi lo sa.

  • Il backtic è l'unica citazione che è veramente utile con lambdas, ma solo se non stai usando il legame lessicale per qualche motivo.


Prendi in considerazione l'aggiunta di un collegamento alla sezione Funzioni anonime del manuale che contiene un esempio che spiega l'effetto della quotazione sulla compilazione di byte.
Costantino

@Constantine Done. Sono diventato pigro perché sono al telefono e l'OP lo ha già collegato comunque.
Malabarba,

Puoi chiarire cosa intendi per non usare il legame lessicale e il backtick? Grazie.
coredump,

@coredump Con l'associazione dinamica, l'unico modo per rendere accessibili le variabili esterne all'interno di una lambda è costruire manualmente la lambda come un elenco con la variabile all'interno. I retroscena fanno bene a quel genere di cose.
Malabarba,

A proposito, non credo che "una volta" si applichi davvero: quando ho studiato questo argomento nella cronologia delle revisioni, ho scoperto che lambdaè stata definita come una macro che aggiunge il functionpiù indietro possibile. IOW, se #'è stato necessario ad un certo punto, era in un codice di sviluppo molto precoce. Di certo non era necessario già in Emacs-18.
Stefan,

5

Dal momento che lambdanon ha alcun senso quando non è citato, le recenti versioni di Emacs Lisp seguono (ANSI) Common Lisp nell'interpretazione non quotata (lambda...)come #'(lambda...). Le due notazioni sono quasi esattamente equivalenti (tranne all'interno della struttura quotata).

Se preferire (lambda...)o #'(lambda...)è quindi puramente una questione di stile. Alcune persone preferiscono la forma nuda, che evita il rumore sintattico, mentre altre (incluso me stesso) preferiscono la forma citata.


Ciò contraddice il manuale di elisp: "In Emacs Lisp, tale elenco è un'espressione valida che valuta un oggetto funzione".
Djechlin,

8
"Versioni recenti" come in "versioni rilasciate dopo circa il 1990" ;-)
Stefan

0

Aggiungendo un po 'di storia in più, a causa del vedere l' eredità storica di Is #' (lambda ...)?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=4290 suggerisce che:

A partire da Emacs 22, un lambdamodulo viene compilato in byte quando viene utilizzato come funzione, indipendentemente dal fatto che sia preceduto da functiono #'. Con le versioni di Emacs precedenti alla 22, è necessario utilizzare esplicitamente #' o functionse si desidera che il modulo sia compilato in byte.

Non conosco il compilatore di byte, ma posso vedere che almeno nel 1993 la lambdamacro stessa ha restituito un (function (lambda ...))modulo.

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

È interessante notare che (diversamente da MacLisp), lambdatecnicamente non faceva parte del linguaggio Elisp fino al 1991 circa, quando fu aggiunto come macro, all'inizio dello sviluppo di Emacs-19. In Emacs-18, le funzioni anonime sono state scritte come valori citati nel modulo:

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

Mentre la lambdamacro ha reso questa citazione non necessaria per quasi 30 anni ormai, molte istanze di questa pratica si verificano ancora nel codice Elisp, anche se impedisce la compilazione di byte del corpo. In qualche modo correlato, solo nel 1993 Lucid Emacs 19.8 ha importato la #'...stenografia del lettore (function ...)da MacLisp. Emacs lo seguì l'anno successivo.


0

Voglio solo fare un esempio pratico dell'uso dell'espressione lambda backtic. Riguarda il legame lessicale / shadowing variabile, l'uso di un'espressione lambda backtic e il riferimento a variabili con una virgola consente di ottenere il loro valore globale.

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

M-x [RET] eval-buffer genera "Random old"

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

M-x [RET] eval-buffer genera "Casuale"

Un terzo esempio che combina la variabile globale e la variabile locale della variabile

;; -*- 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 genera "Casuale" "Casuale vecchio"


@npostavs che non era il punto con il mio esempio ma ho modificato il mio esempio per evitare anche quella cattiva pratica
cjohansson

Meglio, anche se non sono ancora convinto che questo sia un miglioramento rispetto alla sola scelta di un nome diverso per il legame interno.
npostavs,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.