Wann sollte man einen Lambda-Ausdruck scharf zitieren?

30

F: Wann, wenn überhaupt, ist es sinnvoll, a scharf zu zitieren lambda, und wann, wenn überhaupt, müssen wir a nicht scharf zu zitieren lambda?

Die Leute benutzen lambdas auf drei Arten:

  1. einfach: (lambda (x) x)
  2. zitiert: '(lambda (x) x)
  3. scharf zitiert: #'(lambda (x) x)

In diesem SO-Thread werden die drei Typen erörtert, in diesem SO-Thread wird erläutert, warum nicht zitiert werden soll (NB: not sharp quote ) lambda, und in diesem SO-Thread werden auch die Unterschiede zwischen Zitieren und scharfem Zitieren erörtert.

Nun, der manuelle Knoten für anonyme Funktionen und der Docstring für die lambdaNotiz, dass lambdas sich selbst zitieren:

Ein Aufruf des Formulars (lambda ARGS DOCSTRING INTERACTIVE BODY)ist selbsterklärend; Das Ergebnis der Auswertung des Lambda-Ausdrucks ist der Ausdruck selbst. Der Lambda-Ausdruck kann dann als eine Funktion behandelt werden ...

Es scheint also, dass (lambda (x) x)und #'(lambda (x) x)äquivalent sind, ist es aber '(lambda (x) x)nicht (am wichtigsten beim Kompilieren von Bytes).

Es sieht aus wie würde man selten wollen , zitiert ein lambda, aber es ist mir unklar, wann, wenn überhaupt, sollten wir oder sollte nicht scharf Zitat:

  • Ist scharfes Zitieren lambdaeinfach eine stilistische Wahl, oder gibt es Umstände, unter denen scharfes Zitieren tatsächlich nützlich ist?
  • Gibt es Umstände, unter denen wir a nicht scharf zitieren dürfen lambda, das heißt, wenn dies die Bedeutung des Codes verändern würde?
Dan
quelle

Antworten:

28

Es war einmal ein scharfes Zitat für Lambdas, jetzt ist das nicht mehr der Fall.

Es scheint also, dass (Lambda (x) x) und # '(Lambda (x) x) äquivalent sind, aber' (Lambda (x) x) ist nicht (am wichtigsten beim Kompilieren von Bytes).

Ja. Tatsächlich sind die ersten beiden bei der Auswertung völlig identisch. Wie in der von Ihnen verlinkten Manpage beschrieben :

Die folgenden Formulare sind alle gleichwertig:

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

Abgesehen von dem Versuch, Emacs-Versionen von vor zwei Jahrzehnten zu unterstützen, gibt es keinen Grund, ein Lambda scharf zu zitieren.

Also nicht.


Als Randnotiz:

  • Das harte Zitieren eines Lambda (mit ') macht einen Unterschied, es verhindert die Byte-Kompilierung. Ich kann mir kein Szenario vorstellen, in dem das nützlich ist, aber wer weiß.

  • Das Backtic ist das einzige Zitat, das für Lambdas wirklich nützlich ist, aber nur, wenn Sie aus irgendeinem Grund keine lexikalische Bindung verwenden.

Malabarba
quelle
Fügen Sie möglicherweise einen Link zum Abschnitt " Anonyme Funktionen " des Handbuchs hinzu, der ein Beispiel enthält, in dem die Auswirkung von Anführungszeichen auf die Bytekompilierung erläutert wird.
Constantine
@ Konstantin Fertig. Ich bin faul geworden, weil ich telefoniert habe und das OP es sowieso schon verbunden hat.
Malabarba
Können Sie klarstellen, was Sie damit meinen, dass Sie keine lexikalische Bindung und kein Backtick verwenden? Vielen Dank.
Coredump
@coredump Mit dynamischer Bindung können Sie externe Variablen innerhalb eines Lambdas nur dann zugänglich machen, wenn Sie das Lambda manuell als Liste mit der darin enthaltenen Variablen erstellen. Backtics sind gut für so etwas.
Malabarba
Übrigens, ich denke nicht, dass "Es war einmal" wirklich zutrifft: Als ich dieses Thema in der Revisionshistorie untersuchte, stellte ich fest, dass lambdaes sich um ein Makro handelt, das das functionfür so zurück wie möglich hinzufügt . IOW, wenn #'es irgendwann gebraucht wurde, befand es sich in einem sehr frühen Entwicklungscode. Es wurde sicher nicht schon in Emacs-18 benötigt.
Stefan
5

Da lambdaes keinen Sinn macht, wenn es nicht zitiert wird, folgen neuere Versionen von Emacs Lisp (ANSI) Common Lisp bei der Interpretation (lambda...)als nicht zitiert #'(lambda...). Die beiden Notationen sind fast genau gleich (außer innerhalb der angegebenen Struktur).

Ob Sie es bevorzugen (lambda...)oder nicht, #'(lambda...)ist daher eine reine Stilfrage. Einige Leute bevorzugen die nackte Form, die syntaktisches Rauschen vermeidet, während andere (einschließlich ich) die zitierte Form bevorzugen.

jch
quelle
Dies widerspricht dem elisp-Handbuch: "In Emacs Lisp ist eine solche Liste ein gültiger Ausdruck, der zu einem Funktionsobjekt ausgewertet wird."
Djechlin
8
"Recent versions" as in "versions released after 1990 or so" ;-)
Stefan
0

Hinzufügen einer zusätzlichen Geschichte, um das historische Erbe von Is # '(lambda ...) zu sehen?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=4290 schlägt Folgendes vor:

Beginnend mit Emacs 22, eine lambdaform byte-kompiliert , wenn sie als eine Funktion verwendet wird, und zwar unabhängig davon , ob sie durch vorausgeht functionoder #'. Bei Emacs-Versionen vor 22 müssen Sie explizit verwenden, #' oder functionwenn Sie möchten, dass das Formular bytegemischt wird.

Ich weiß nichts über den Byte-Compiler, aber ich kann feststellen, dass das lambdaMakro selbst bereits 1993 ein (function (lambda ...))Formular zurückgegeben hat.

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

Interessanterweise (im Gegensatz zu MacLisp) lambdawar es nicht Teil der Elisp-Sprache, bis es 1991 als Makro zu Beginn der Entwicklung von Emacs-19 hinzugefügt wurde. In Emacs-18 wurden anonyme Funktionen als Anführungszeichen der folgenden Form geschrieben:

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

Obwohl das lambdaMakro dieses Zitat seit fast 30 Jahren überflüssig macht, treten viele Beispiele dieser Praxis im Elisp-Code auf, obwohl es die Byte-Kompilierung des Körpers verhindert. In gewisser Weise importierte Lucid Emacs 19.8 erst 1993 die #'...Abkürzung für Reader (function ...)von MacLisp. Emacs folgte im Jahr danach.

Phils
quelle
0

Ich möchte nur ein praktisches Beispiel für die Verwendung des backtischen Lambda-Ausdrucks geben. Es geht um lexikalisches Binden / Variablen-Shadowing, die Verwendung eines Lambda-Ausdrucks und die Referenzierung von Variablen mit einem Komma ermöglichen es, ihren globalen Wert zu ermitteln.

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

M-x [RET] eval-buffer gibt "Random old" aus

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

M-x [RET] eval-buffer gibt "Random" aus

Ein drittes Beispiel, das eine globale Variable und eine lokale Variable der Variablen kombiniert

;; -*- 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 gibt "Random" aus "Random old"

cjohansson
quelle
@npostavs das war nicht der Punkt mit meinem Beispiel, aber ich habe mein Beispiel geändert, um diese schlechte Praxis auch zu vermeiden
cjohansson
Besser, obwohl ich immer noch nicht davon überzeugt bin, dass dies eine Verbesserung ist, als nur einen anderen Namen für die innere Bindung zu wählen.
Npostavs