Wann wird '(oder Zitat) in Lisp verwendet?

114

Nachdem ich die Hauptteile eines einführenden Lisp-Buches durchgearbeitet hatte, konnte ich immer noch nicht verstehen, was die spezielle Operatorfunktion (quote)(oder eine gleichwertige 'Funktion) tut, aber dies war der gesamte Lisp-Code, den ich gesehen habe.

Was tut es?

Cristián Romo
quelle

Antworten:

178

Kurze Antwort Bypass die Bewertungsregeln Ausfall- und tut nicht bewertet den Ausdruck (Symbol oder s-exp), vorbei entlang auf die Funktion genau wie eingegeben.

Lange Antwort: Die Standardbewertungsregel

Wenn eine reguläre Funktion (darauf komme ich später zurück) aufgerufen wird, werden alle an sie übergebenen Argumente ausgewertet. Dies bedeutet, dass Sie Folgendes schreiben können:

(* (+ a 2)
   3)

Was wiederum (+ a 2)durch Auswertung aund 2 ausgewertet wird . Der Wert des Symbols awird im aktuellen Variablenbindungssatz nachgeschlagen und dann ersetzt. Say aist derzeit an den Wert 3 gebunden:

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

Wir würden bekommen (+ 3 2), + wird dann auf 3 und 2 aufgerufen, was 5 ergibt. Unsere ursprüngliche Form ergibt jetzt (* 5 3)15.

Erklären Sie quotebereits!

In Ordung. Wie oben gezeigt, werden alle Argumente für eine Funktion ausgewertet. Wenn Sie also das Symbol a und nicht seinen Wert übergeben möchten, möchten Sie es nicht auswerten. Lisp-Symbole können sowohl als Werte als auch als Markierungen verwendet werden, bei denen Sie in anderen Sprachen Zeichenfolgen verwendet hätten, z. B. Schlüssel für Hash-Tabellen.

Hier quotekommt das Spiel ins Spiel. Angenommen, Sie möchten Ressourcenzuweisungen aus einer Python-Anwendung zeichnen, aber das Zeichnen in Lisp durchführen. Lassen Sie Ihre Python-App etwa Folgendes tun:

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

Geben Sie Ihre Ausgabe so (leicht hübsch):

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

Erinnern Sie sich, was ich über quote("Häkchen") gesagt habe, wodurch die Standardregel nicht angewendet wurde? Gut. Was sonst passieren würde, ist, dass die Werte von allocateund freenachgeschlagen werden, und das wollen wir nicht. In unserem Lisp möchten wir Folgendes tun:

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

Für die oben angegebenen Daten wäre die folgende Folge von Funktionsaufrufen durchgeführt worden:

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

Aber was ist mit list?

Nun, manchmal Sie tun wollen , die Argumente zu bewerten. Angenommen, Sie haben eine raffinierte Funktion, die eine Zahl und eine Zeichenfolge bearbeitet und eine Liste der resultierenden ... Dinge zurückgibt. Machen wir einen Fehlstart:

(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))

Hallo! Das wollten wir nicht. Wir wollen einige Argumente selektiv bewerten und die anderen als Symbole belassen. Versuchen Sie # 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)

Nicht nur quote, sondernbackquote

Viel besser! Übrigens ist dieses Muster in (meistens) Makros so häufig, dass es dafür eine spezielle Syntax gibt. Das Backquote:

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

Es ist wie mit quote, aber mit der Option, einige Argumente explizit auszuwerten, indem ihnen ein Komma vorangestellt wird. Das Ergebnis entspricht der Verwendung list. Wenn Sie jedoch Code aus einem Makro generieren, möchten Sie häufig nur kleine Teile des zurückgegebenen Codes auswerten, sodass das Backquote besser geeignet ist. listKann für kürzere Listen besser lesbar sein.

Hey, du hast es vergessen quote!

Wo bleibt uns das? Oh richtig, was macht quoteeigentlich? Es gibt einfach seine Argumente zurück, die nicht bewertet wurden! Erinnerst du dich, was ich am Anfang über reguläre Funktionen gesagt habe? Es stellt sich heraus, dass einige Operatoren / Funktionen ihre Argumente nicht bewerten müssen. Zum Beispiel IF - Sie möchten nicht, dass der Zweig else ausgewertet wird, wenn er nicht verwendet wird, oder? So arbeiten sogenannte Spezialoperatoren zusammen mit Makros. Spezielle Operatoren sind auch das "Axiom" der Sprache - minimale Regeln -, nach denen Sie den Rest von Lisp implementieren können, indem Sie sie auf unterschiedliche Weise miteinander kombinieren.

Zurück zu quote:

Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL

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

Vergleiche mit (auf 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] 

Weil es spiffy-symbolim aktuellen Umfang keine gibt !

Zusammenfassen

quote, backquote(mit Komma) und listsind einige der Werkzeuge, die Sie zum Erstellen von Listen verwenden. Dies sind nicht nur Wertelisten, sondern können, wie Sie gesehen haben, als einfache structDatenstrukturen verwendet werden (Sie müssen keine definieren )!

Wenn Sie mehr erfahren möchten, empfehle ich Peter Seibels Buch Practical Common Lisp für einen praktischen Ansatz zum Erlernen von Lisp, wenn Sie bereits mit dem Programmieren auf freiem Fuß beschäftigt sind. Schließlich werden Sie auf Ihrer Lisp-Reise auch Pakete verwenden. Ron Garrets The Idiot's Guide zu gängigen Lisp-Paketen gibt Ihnen eine gute Erklärung dafür.

Viel Spaß beim Hacken!

Mikael Jansson
quelle
In meinem Emacs ist SBCL eingerichtet und wenn ich "this" tippe, ist "true". Es wird nur das letzte, dh TRUE in der Ausgabe zurückgegeben. Sogar in Portacle bekomme ich die gleiche Ausgabe
Totoro
@Totoro Der Rückgabewert einer Funktion oder nur in Lisp mehrere Anweisungen ist der letzte Ausdruck, so dass er tatsächlich zurückkehrt this, dann is, dann true, aber nur die letzten zurück sehen. (Dies ist und wahr sind separate Aussagen)
Wezl
52

Es heißt "bewerte mich nicht". Wenn Sie beispielsweise eine Liste als Daten und nicht als Code verwenden möchten, setzen Sie ein Zitat davor. Beispielsweise,

(print '(+ 3 4))druckt "(+ 3 4)", während (print (+ 3 4))"7"

Adam Rosenfield
quelle
Wie könnte es dann ausgewertet werden, gibt es zum Beispiel einen unquoteBefehl?
Kalk
3
@ William Lisps haben eine praktische Funktion namens eval: (print (eval '(+ 3 4))). Dies macht Lisps so großartig: Listen sind Code und Code sind Listen, sodass ein Lisp-Programm sich selbst manipulieren kann.
Darkfeline
18

Andere Leute haben diese Frage bewundernswert beantwortet, und Matthias Benkard bringt eine ausgezeichnete Warnung vor.

Verwenden Sie kein Zitat, um Listen zu erstellen, die Sie später ändern werden. Die Spezifikation ermöglicht es dem Compiler, Listen in Anführungszeichen als Konstanten zu behandeln. Oft optimiert ein Compiler Konstanten, indem er einen einzelnen Wert für sie im Speicher erstellt und dann von allen Stellen, an denen die Konstante angezeigt wird, auf diesen einzelnen Wert verweist. Mit anderen Worten, es kann die Konstante wie eine anonyme globale Variable behandeln.

Dies kann offensichtliche Probleme verursachen. Wenn Sie eine Konstante ändern, können möglicherweise auch andere Verwendungen derselben Konstante in völlig unabhängigem Code geändert werden. Sie können beispielsweise eine Variable in einer Funktion mit '(1 1) vergleichen und in einer völlig anderen Funktion eine Liste mit' (1 1) beginnen und dann weitere Elemente hinzufügen. Wenn Sie diese Funktionen ausführen, stellen Sie möglicherweise fest, dass die erste Funktion nicht mehr richtig übereinstimmt, da jetzt versucht wird, die Variable mit '(1 1 2 3 5 8 13) zu vergleichen, was die zweite Funktion zurückgegeben hat. Diese beiden Funktionen sind völlig unabhängig voneinander, wirken sich jedoch aufgrund der Verwendung von Konstanten gegenseitig aus. Sogar verrücktere schlechte Effekte können auftreten, wie eine ganz normale Listeniteration, die plötzlich eine Endlosschleife aufweist.

Verwenden Sie das Anführungszeichen, wenn Sie eine konstante Liste benötigen, z. B. zum Vergleich. Verwenden Sie die Liste, wenn Sie das Ergebnis ändern möchten.

Xanthir
quelle
Es hört sich also so an, als ob Sie die (list (+ 1 2)) meiste Zeit verwenden sollten. Wenn ja, wie verhindern Sie die Bewertung eines (+ 1 2)solchen Beispiels? Gibt es einen unquoteBefehl?
Kalk
1
Möchten Sie das Äquivalent von '((3))oder das Äquivalent von '((+ 1 2))? In letzterem Fall müssen Sie mehr verwenden list: (list (list '+ 1 2)). Oder wenn Sie das Äquivalent von '(+ 1 2)nur wollten (list '+ 1 2). Und denken Sie daran, wenn Sie die Liste nicht ändern, können Sie auch das Zitat verwenden: Es ist nichts Falsches daran, wenn Sie nur mit der Liste '(+ 1 2)vergleichen oder so.
Xanthir
1
Haben Sie etwas dagegen, darauf zu verweisen, wo Listen in Anführungszeichen als Konstanten behandelt werden sollen?
Kalk
Die HyperSpec clhs.lisp.se/Body/s_quote.htm besagt, dass das Verhalten undefiniert ist, wenn das angegebene Objekt destruktiv geändert wird. Dies impliziert, dass Impls die Werte als Atomwerte behandeln können.
Xanthir
14

Eine Antwort auf diese Frage besagt, dass QUOTE „Listendatenstrukturen erstellt“. Das ist nicht ganz richtig. QUOTE ist grundlegender als dies. Tatsächlich ist QUOTE ein trivialer Operator: Er soll verhindern , dass überhaupt etwas passiert. Insbesondere schafft es nichts.

Was (ZITAT X) sagt, ist im Grunde: "Tu nichts, gib mir nur X." X muss keine Liste wie in (QUOTE (ABC)) oder ein Symbol wie in (QUOTE FOO) sein. Es kann jedes Objekt sein, was auch immer. In der Tat gibt das Ergebnis der Auswertung der Liste, die von (LIST 'QUOTE SOME-OBJECT) erstellt wurde, immer nur SOME-OBJECT zurück, was auch immer es ist.

Der Grund, warum (QUOTE (ABC)) so aussieht, als hätte es eine Liste erstellt, deren Elemente A, B und C sind, ist, dass eine solche Liste wirklich das ist, was sie zurückgibt. Zum Zeitpunkt der Auswertung des QUOTE-Formulars existiert die Liste jedoch im Allgemeinen bereits seit einiger Zeit (als Bestandteil des QUOTE-Formulars!) und wurde entweder vom Loader oder vom Reader vor der Ausführung des Codes erstellt.

Eine Implikation davon, die Neulinge ziemlich oft auslöst, ist, dass es sehr unklug ist, eine von einem QUOTE-Formular zurückgegebene Liste zu ändern. Von QUOTE zurückgegebene Daten sind in jeder Hinsicht als Teil des ausgeführten Codes zu betrachten und sollten daher als schreibgeschützt behandelt werden!

Matthias Benkard
quelle
11

Das Zitat verhindert die Ausführung oder Auswertung eines Formulars und wandelt es stattdessen in Daten um. Im Allgemeinen können Sie die Daten ausführen, indem Sie sie dann auswerten.

Zitat erstellt Listendatenstrukturen, zum Beispiel sind die folgenden äquivalent:

(quote a)
'a

Es kann auch zum Erstellen von Listen (oder Bäumen) verwendet werden:

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

Am besten holen Sie sich ein Einführungsbuch über Lisp, wie z. B. Practical Common Lisp (das online gelesen werden kann).

Kyle Burton
quelle
3

In Emacs Lisp:

Was kann zitiert werden?

Listen und Symbole.

Das Zitieren einer Nummer ergibt die Nummer selbst: '5ist dasselbe wie 5.

Was passiert, wenn Sie Listen zitieren?

Beispielsweise:

'(one two) bewertet zu

(list 'one 'two) was zu bewertet

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

(intern "one")Erstellt ein Symbol mit dem Namen "Eins" und speichert es in einer "zentralen" Hash-Map. Wenn Sie also sagen, 'onewird das genannte Symbol "one"in dieser zentralen Hash-Map nachgeschlagen.

Aber was ist ein Symbol?

Beispielsweise könnte in OO-Sprachen (Java / Javascript / Python) ein Symbol als ein Objekt dargestellt werden, das ein nameFeld hat, das der Name des Symbols wie "one"oben ist, und diesem Objekt können Daten und / oder Code zugeordnet werden.

Ein Symbol in Python könnte also wie folgt implementiert werden:

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

In Emacs Lisp kann beispielsweise einem Symbol 1) Daten zugeordnet sein UND (gleichzeitig - für dasselbe Symbol) 2) Code zugeordnet werden - je nach Kontext werden entweder die Daten oder der Code aufgerufen.

Zum Beispiel in Elisp:

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

bewertet zu 4.

Weil (add add add)bewertet als:

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

Mit der SymbolKlasse, die wir oben in Python definiert haben, könnte dieses addELisp-Symbol beispielsweise in Python als geschrieben werden Symbol("add",(lambda x,y: x+y),2).

Vielen Dank für die Leute im IRC #emacs, die mir Symbole und Zitate erklärt haben.

jhegedus
quelle
2

Wenn wir ein Argument selbst übergeben möchten, anstatt den Wert des Arguments zu übergeben, verwenden wir das Anführungszeichen. Es hängt hauptsächlich mit der Prozedur zusammen, die während der Verwendung von Listen, Paaren und Atomen ausgeführt wird, die in der Programmiersprache C nicht verfügbar sind (die meisten Leute beginnen mit der Programmierung in der Programmiersprache C, daher werden wir verwirrt). Dies ist Code in der Programmiersprache Schema, der ein Dialekt von lisp ist und ich denke, Sie können diesen Code verstehen.

(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

Die letzte Zeile (atom? 'Abc) übergibt abc wie an die Prozedur, um zu überprüfen, ob abc ein Atom ist oder nicht. Wenn Sie jedoch übergeben (atom? Abc), prüft sie den Wert von abc und übergibt den Wert an es. Seitdem haben wir keinen Wert dafür angegeben

unbekannter Fehler
quelle
1

Quote gibt die interne Darstellung seiner Argumente zurück. Nachdem zu viele Erklärungen durchgearbeitet wurden, was das Zitat nicht tut, ging die Glühbirne an. Wenn die REPL beim Zitieren keine Funktionsnamen in UPPER-CASE konvertiert hätte, wäre es mir möglicherweise nicht aufgefallen.

So. Gewöhnliche Lisp-Funktionen konvertieren ihre Argumente in eine interne Darstellung, werten die Argumente aus und wenden die Funktion an. Quote konvertiert seine Argumente in eine interne Darstellung und gibt diese nur zurück. Technisch ist es richtig zu sagen, dass das Zitat "nicht bewerten" sagt, aber als ich versuchte zu verstehen, was es tat, war es frustrierend, mir zu sagen, was es nicht tut. Mein Toaster bewertet auch keine Lisp-Funktionen. Aber so erklären Sie nicht, was ein Toaster tut.

Steve
quelle
1

Eine weitere kurze Antwort:

quotebedeutet, ohne es zu bewerten, und backquote ist Zitat, aber lassen Sie Hintertüren .

Eine gute Referenz:

Das Emacs Lisp Referenzhandbuch macht es sehr deutlich

9.3 Zitieren

Das spezielle Formularzitat gibt sein einzelnes Argument wie geschrieben zurück, ohne es zu bewerten. Dies bietet die Möglichkeit, konstante Symbole und Listen, die keine selbstbewertenden Objekte sind, in ein Programm aufzunehmen. (Es ist nicht erforderlich, selbstbewertende Objekte wie Zahlen, Zeichenfolgen und Vektoren anzugeben.)

Sonderform: Angebotsobjekt

This special form returns object, without evaluating it. 

Da Anführungszeichen in Programmen so häufig verwendet werden, bietet Lisp eine bequeme Lesesyntax. Ein Apostrophzeichen ('' ') gefolgt von einem Lisp-Objekt (in Lesesyntax) wird zu einer Liste erweitert, deren erstes Element in Anführungszeichen und deren zweites Element das Objekt ist. Somit ist die Lesesyntax 'x eine Abkürzung für (Zitat x).

Hier sind einige Beispiele für Ausdrücke, die Anführungszeichen verwenden:

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

(quote foo)
      foo

'foo
      foo

''foo
      (quote foo)

'(quote foo)
      (quote foo)

9.4 Backquote

Mit Backquote-Konstrukten können Sie eine Liste zitieren, aber Elemente dieser Liste selektiv auswerten. Im einfachsten Fall ist es identisch mit dem speziellen Formularzitat (im vorherigen Abschnitt beschrieben; siehe Zitat). Zum Beispiel liefern diese beiden Formen identische Ergebnisse:

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

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

Die spezielle Markierung ',' innerhalb des Arguments to backquote gibt einen Wert an, der nicht konstant ist. Der Emacs Lisp-Evaluator wertet das Argument ',' aus und fügt den Wert in die Listenstruktur ein:

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

Die Ersetzung durch ',' ist auch auf tieferen Ebenen der Listenstruktur zulässig. Beispielsweise:

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

Sie können einen ausgewerteten Wert auch mit der speziellen Markierung ', @' in die resultierende Liste einfügen. Die Elemente der gespleißten Liste werden zu Elementen auf derselben Ebene wie die anderen Elemente der resultierenden Liste. Der entsprechende Code ohne Verwendung von '`' ist häufig nicht lesbar. Hier sind einige Beispiele:

(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
quelle
1
Code is data and data is code.  There is no clear distinction between them.

Dies ist eine klassische Aussage, die jeder Lisp-Programmierer kennt.

Wenn Sie einen Code zitieren, handelt es sich bei diesem Code um Daten.

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

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

Wenn Sie einen Code zitieren, sind das Ergebnis Daten, die diesen Code darstellen. Wenn Sie also mit Daten arbeiten möchten, die ein Programm darstellen, zitieren Sie dieses Programm. Dies gilt auch für atomare Ausdrücke, nicht nur für Listen:

1 ]=> 'code
;Value: code

1 ]=> '10
;Value: 10

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

1 ]=> code
;Unbound variable: code

Angenommen, Sie möchten eine in lisp eingebettete Programmiersprache erstellen - Sie arbeiten mit Programmen, die im Schema (wie '(+ 2 3)) zitiert sind und in der von Ihnen erstellten Sprache als Code interpretiert werden, indem Sie Programmen eine semantische Interpretation geben. In diesem Fall müssen Sie ein Anführungszeichen verwenden, um die Daten zu speichern. Andernfalls werden sie in einer externen Sprache ausgewertet.

Alinsoar
quelle