Frage

ich brauche etwas Hilfe einige der Punkte von Paul Grahams Verständnis Was hat Lisp Verschiedene .

  1. Ein neues Konzept von Variablen. In Lisp, sind alle Variablen effektiv Zeiger. Die Werte sind, welche Arten haben, keine Variablen und Zuweisen oder Variablen mittels Kopieren Zeiger zu binden, nicht das, was sie zeigen auf.

  2. Ein Symboltyp. Symbole unterscheiden sich von Strings, dass Sie Gleichheit durch den Vergleich eines Zeigers testen können.

  3. Eine Notation für die Code-Bäume von Symbolen verwendet wird.

  4. Die ganze Sprache immer zur Verfügung. Es gibt keine wirkliche Unterscheidung zwischen Lesezeit, Compile-Zeit und der Laufzeit. Sie können kompilieren oder Ausführen von Code beim Lesen, Lesen oder Ausführen von Code beim Kompilieren und lesen oder Kompilierung Code zur Laufzeit.

Was tun diese Punkte bedeuten? Wie unterscheiden sie sich in Sprachen wie C oder Java? Haben alle anderen Sprachen anders als Lisp Familie Sprachen haben alle diese Konstrukte jetzt?

War es hilfreich?

Lösung

Matts Erklärung ist völlig in Ordnung - und er nimmt einen Schuss auf einem Vergleich zu C und Java, die ich nicht tun - aber aus irgendeinem Grunde, den ich genieße wirklich dieses Thema sehr ab und zu diskutieren, so - hier ist meine Chance auf eine Antwort.

Auf Punkten (3) und (4):

Punkte (3) und (4) auf der Liste erscheinen die interessantesten und noch heute relevant.

Um sie zu verstehen, ist es hilfreich, sich ein klares Bild von dem, was passiert mit dem Code Lisp - in Form eines Stroms von Zeichen in der Programmierer eingegeben - auf dem Weg ausgeführt werden. Lassen Sie sich verwenden, um ein konkretes Beispiel:

;; a library import for completeness,
;; we won't concern ourselves with it
(require '[clojure.contrib.string :as str])

;; this is the interesting bit:
(println (str/replace-re #"\d+" "FOO" "a123b4c56"))

Dieser Code-Schnipsel von Clojure Code druckt aFOObFOOcFOO. Beachten Sie, dass Clojure ist wohl nicht in vollem Umfang den vierten Punkt auf Ihrer Liste erfüllen, da Lesezeit Benutzercode nicht wirklich offen ist; Ich werde diskutieren, was es bedeuten würde, für diese sonst sein, aber.

Also, nehmen wir diesen Code in einer Datei irgendwo haben, und wir bitten Clojure es auszuführen. Auch sie (aus Gründen der Einfachheit) geht davon aus, dass wir es vorbei an der Bibliothek importiert gemacht haben. Das interessante Bit beginnt bei (println und endet am ) weit nach rechts. Dies ist gelext / geparsten wie man erwarten würde, aber schon ein wichtiger Punkt stellt sich: ist das Ergebnis nicht einige spezielle Compiler-spezifische AST Darstellung - es ist nur eine ganz normale Clojure / Lisp Datenstruktur , nämlich eine verschachtelte Liste eine Reihe von Symbolen, Zeichenfolgen und enthält - in diesem Fall - eine einzelne kompilierten RegexMuster Objekt entsprechend dem #"\d+" literal (mehr dazu unten). Einige Lisps fügen ihre eigenen kleine Drehungen zu diesem Prozess, aber Paul Graham wurde vor allem mit Bezug auf Common Lisp. An den Punkten zu Ihrer Frage, ist Clojure ähnlich wie CL.

Die ganze Sprache bei der Kompilierung:

Nach diesem Punkt alle der Compiler behandelt (dies kann auch für ein Lisp-Interpreter wahr wäre, Clojure Code geschieht immer kompiliert wird) sind Lisp Datenstrukturen, die Lisp-Programmierer zu manipulieren verwendet werden. An dieser Stelle eine wunderbare Möglichkeit wird deutlich, warum erlaubt nicht Lisp Programmierer Lisp-Funktionen zu schreiben, die Lisp von Daten, die Lisp-Programme und Ausgang transformierte Daten repräsentieren transformierten Programme manipulieren, um anstelle der Originale verwendet werden? Mit anderen Worten - erlauben, warum nicht Lisp Programmierer ihre Funktionen als Compiler Plugins von Arten zu registrieren, die so genannte Makros in Lisp? Und in der Tat jeden anständiges Lisp-System hat diese Fähigkeit.

So, Makros sind regelmäßige Funktionen Lisp auf das Programm der Darstellung bei der Kompilierung arbeiten, vor der endgültigen Zusammenstellung Phase, wenn die tatsächliche Objektcode ausgegeben wird. Da es keine Grenzen für die Arten von Code-Makros ausgeführt werden darf (insbesondere der Code, den sie laufen oft selbst mit großzügigem Einsatz der Makrofunktion geschrieben), kann man sagen, dass „die ganze Sprache ist bei der Kompilierung verfügbar “.

Die ganze Sprache bei Lesezeit:

Gehen wir zurück zu diesem #"\d+" regex wörtlich zu nehmen. Wie oben erwähnt, wird bei Lesezeit zu einem tatsächlichen kompilierte Musterobjekt verwandelt diese, bevor der Compiler die erste Erwähnung neuer Code hört für die Kompilierung vorbereitet. Wie funktioniert das passieren?

Nun, die Art und Weise Clojure derzeit umgesetzt wird, ist das Bild etwas anders als das, was Paul Graham im Sinne hatte, obwohl alles ist möglich mit ein kluger Hack . In Common Lisp, wäre die Geschichte etwas konzeptionell sauber. Die Grundlagen sind jedoch ähnlich: Die Lisp Reader ist eine Zustandsmaschine, die, zusätzlich zu den Zustandsübergänge durchführt und schließlich erklären, ob er eine „akzeptierenden Zustand“ erreicht hat, spuckt Lisp Datenstrukturen die Zeichen darstellen. So 123 die Zeichen der Nummer werden 123 usw. Der Important Punkt kommt jetzt: diese Zustandsmaschine durch Benutzercode geändert werden kann . (Wie bereits erwähnt, dass in CL Fall ganz wahr ist;. Für Clojure, ein Hack (entmutigt & nicht in der Praxis verwendet wird) erforderlich ist, aber ich schweife ab, es ist PGs Artikel ich soll die Ausarbeitung werden, so ...)

Wenn Sie also ein Common Lisp-Programmierer sind und Sie gerade die Idee von Clojure-Stil Vektor-Literale möchten, können Sie einfach Stecker in den Leser eine Funktion in geeigneter Weise zu einem gewissen Zeichensequenz zu reagieren - [ oder #[ möglicherweise - - und behandeln sie als Beginn eines Vektors wörtliche ] an der passenden endet. Eine solche Funktion ist ein Leser Makro genannt und wie eine normale Makro, kann es jede Art von Lisp-Code auszuführen, Code enthält, die sich mit funky Schreibweise geschrieben wurde, indem zuvor registrierte Leser Makros aktiviert. So gibt es die ganze Sprache bei Lesezeit für Sie.

Verpackung zusammen:

Eigentlich, was bisher gezeigt worden ist, dass eine regelmäßige Lisp Funktionen bei Lesezeit oder der Kompilierung ausgeführt werden können; der ein Schritt braucht man von hier zum Verständnis zu nehmen, wie das Lesen und Übersetzen selbst möglich zu lesen, die Kompilierung oder Laufzeit ist, dass das Lesen zu erkennen, und die Zusammenstellung selbst von Lisp-Funktionen ausgeführt. Sie können nur read oder eval jederzeit anrufen in Lisp Daten von Zeichenströmen zu lesen oder kompilieren & Lisp Code auszuführen ist. Das ist die ganze Sprache genau dort, die ganze Zeit.

Beachten Sie, wie die Tatsache, dass Lisp erfüllt Punkt (3) aus der Liste auf die Art und Weise wesentlich ist, in dem es Punkt (4) zu erfüllen, schafft - der besondere Geschmack von Makros durch Lisp auf Code stark angewiesen bereitgestellt durch regelmäßige dargestellt wird Lisp-Daten, die etwas durch (3) aktiviert. Im Übrigen nur der „Baum-ish“ Aspekt des Codes ist hier wirklich entscheidend -. Sie möglicherweise ein Lisp XML geschrieben haben könnte

Andere Tipps

1) Ein neues Konzept von Variablen. In Lisp, sind alle Variablen effektiv Zeiger. Die Werte sind, welche Arten haben, keine Variablen und Zuweisen oder Variablen mittels Kopieren Zeiger zu binden, nicht das, was sie zeigen auf.

(defun print-twice (it)
  (print it)
  (print it))

'es' ist eine Variable. Es kann auf einen beliebigen Wert gebunden werden. Es gibt keine Beschränkung und keinen Typen mit den Variablen verbunden. Wenn Sie die Funktion aufrufen, wird das Argument nicht kopiert werden müssen. Die Variable ist ähnlich einem Zeiger. Er hat einen Weg, um den Wert zuzugreifen, der an die Variable gebunden ist. Es gibt keine Notwendigkeit, Reserve Speicher. Wir können jedes Datenobjekt übergeben, wenn wir die Funktion aufrufen. Jeder Größe und jeder Art

Die Datenobjekte haben einen ‚Typ‘ und alle Datenobjekte können für ihre ‚Typ‘ abgefragt werden.

(type-of "abc")  -> STRING

2) Ein Symboltyp. Symbole unterscheiden sich von Strings, dass Sie Gleichheit durch den Vergleich eines Zeigers testen können.

Ein Symbol ist ein Datenobjekt mit einem Namen. Normalerweise kann der Name verwendet werden, um das Objekt zu finden:

|This is a Symbol|
this-is-also-a-symbol

(find-symbol "SIN")   ->  SIN

Da Symbole reale Datenobjekte sind, können wir testen, ob sie das gleiche Objekt sind:

(eq 'sin 'cos) -> NIL
(eq 'sin 'sin) -> T

Dies erlaubt uns zum Beispiel einen Satz mit Symbolen zu schreiben:

(defvar *sentence* '(mary called tom to tell him the price of the book))

Jetzt können wir die Anzahl der in dem Satz zählen:

(count 'the *sentence*) ->  2

In Common Lisp Symbole nicht nur einen Namen haben, aber sie können auch einen Wert haben, eine Funktion, eine Eigenschaftsliste und ein Paket. So Symbole verwendet werden können, Variablen oder Funktionen zu nennen. Die Eigenschaftsliste wird in der Regel verwendet, um Meta-Daten-Symbole hinzuzufügen.

3) Eine Notation für die Code-Bäume von Symbolen verwendet wird.

Lisp verwendet, um seine grundlegenden Datenstrukturen Code darzustellen.

Die Liste (* 3 2) kann beiden Daten und Code:

(eval '(* 3 (+ 2 5))) -> 21

(length '(* 3 (+ 2 5))) -> 3

Der Baum:

CL-USER 8 > (sdraw '(* 3 (+ 2 5)))

[*|*]--->[*|*]--->[*|*]--->NIL
 |        |        |
 v        v        v
 *        3       [*|*]--->[*|*]--->[*|*]--->NIL
                   |        |        |
                   v        v        v
                   +        2        5

4) Die ganze Sprache immer zur Verfügung. Es gibt keine wirkliche Unterscheidung zwischen Lesezeit, Compile-Zeit und der Laufzeit. Sie können kompilieren oder Ausführen von Code beim Lesen, Lesen oder Ausführen von Code beim Kompilieren und lesen oder Kompilierung Code zur Laufzeit.

Lisp bietet die Funktionen Lesen, Daten und Code von Text zu lesen, LOAD zu laden Code, EVAL Code auszuwerten, um Code und PRINT, um Schreibdaten und Code, um Text zu erstellen.

Diese Funktionen sind immer verfügbar. Sie gehen nicht weg. Sie können Teil eines Programms sein. Das bedeutet, dass jedes Programm lesen kann, laden, eval oder Druckcode -. Immer

Wie unterscheiden sie sich in Sprachen wie C oder Java?

Diese Sprachen bieten keine Symbole, Code als Daten oder Laufzeitauswertung von Daten als Code. Datenobjekte in C sind in der Regel nicht typisiert.

Stellen Sie keine anderen Sprachen außer LISP Familie Sprachen haben alle diese Konstrukte jetzt?

Viele Sprachen haben einige dieser Fähigkeiten.

Der Unterschied:

In Lisp diese Funktionen sind in die Sprache so konzipiert, dass sie einfach zu bedienen sind.

Für die Punkte (1) und (2), er spricht historisch. Java-Variablen sind so ziemlich das gleiche, weshalb Sie anrufen .equals müssen () Werte zu vergleichen.

(3) spricht von S-Ausdrücken. Lisp-Programme werden in dieser Syntax geschrieben, die viele Vorteile gegenüber Ad-hoc-Syntax wie Java und C, wie zum Beispiel der Erfassung wiederholte Mustern in Makros in eine viel sauberen Weise als C-Makros oder C ++ Vorlagen und Manipulation von Codes mit der gleichen Kern-Liste bietet Operationen, die Sie für Daten verwenden.

(4) C zum Beispiel nehmen: die Sprache ist wirklich zwei verschiedene Unter Sprachen: Sachen wie if () und while () und der Prä-Prozessor. Sie verwenden den Präprozessor, um sich zu wiederholen, die ganze Zeit sparen mit oder Code mit # überspringen, wenn / # ifdef. Aber beide Sprachen sind ganz getrennt, und Sie können nicht verwenden, während () bei der Kompilierung wie Sie #IF können.

C ++ macht dies noch schlimmer mit Vorlagen. Schauen Sie sich ein paar Hinweise auf Metaprogrammierung aus, die zur Erzeugung von Code während der Kompilierung eine Möglichkeit bietet, und ist extrem schwierig für Nicht-Experten ihre Köpfe herum zu wickeln. Darüber hinaus ist es wirklich eine Reihe von Hacks und Tricks Vorlagen und Makros, dass der Compiler nicht erstklassige Unterstützung für zur Verfügung stellen kann -. Wenn Sie einen einfachen Syntax-Fehler machen, der Compiler nicht in der Lage ist, Ihnen eine klare Fehlermeldung geben

Nun, mit Lisp, haben Sie alles in einer einzigen Sprache. Sie verwenden das gleiche Material Code zur Laufzeit zu erzeugen, wie Sie in Ihrem ersten Tag lernen. Dies ist nicht zu deuten darauf hin, metaprogramming trivial ist, aber es ist sicherlich einfacher mit First-Class-Sprache und Compiler-Unterstützung.

Punkte (1) und (2) würden auch Python passen. Ein einfaches Beispiel „a = Str (82,4)“ der Interpreter erzeugt zuerst ein Gleitkomma-Objekt mit dem Wert 82,4. Dann ruft es ein String-Konstruktor, der dann einen String mit Wert zurückgibt '82 0,4' . Das ‚A‘ auf der linken Seite ist nur ein Etikett für diesen String-Objekt. Das ursprüngliche Floating-Point-Objekt wurde Müll gesammelt, da es keine weiteren Hinweise auf sie sind.

In Schema alles als ein Objekt in ähnlicher Weise behandelt. Ich bin über Common Lisp nicht sicher. Ich würde in Bezug auf den C / C ++ Konzepte zu vermeiden denken versuchen. Sie verlangsamte mich hinunter Haufen, als ich versuchte, meinen Kopf um die schöne Einfachheit der Lisps zu erhalten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top