Mit Common Lisp können Sie Makros schreiben, die die gewünschte Quelltransformation ausführen.
Das Schema gibt Ihnen ein hygienisches Musteranpassungssystem, mit dem Sie auch Transformationen durchführen können. Wie nützlich sind Makros in der Praxis? Paul Graham sagte in Beating the Averages :
Der Quellcode des Viaweb-Editors bestand wahrscheinlich zu 20-25% aus Makros.
Was machen die Leute eigentlich mit Makros?
Antworten:
Werfen Sie einen Blick auf diesen Beitrag von Matthias Felleisen zur LL1-Diskussionsliste im Jahr 2002. Er schlägt drei Hauptverwendungen für Makros vor:
quelle
Ich benutze meistens Makros, um zeitsparende neue Sprachkonstrukte hinzuzufügen, für die sonst ein Haufen Code erforderlich wäre.
Zum Beispiel wollte ich kürzlich einen Imperativ
for-loop
, der C ++ / Java ähnelt. Da Clojure jedoch eine funktionale Sprache ist, wurde sie nicht sofort geliefert. Also habe ich es einfach als Makro implementiert:Und jetzt kann ich tun:
Und da haben Sie es - ein neues Allzweck-Sprachkonstrukt zur Kompilierungszeit in sechs Codezeilen.
quelle
Spracherweiterungen oder DSLs schreiben.
Um ein Gefühl dafür in Lisp-ähnlichen Sprachen zu bekommen, studieren Sie Racket , das verschiedene Sprachvarianten bietet: Typed Racket, R6RS und Datalog.
Siehe auch die Boo-Sprache, mit der Sie auf die Compiler-Pipeline zugreifen können, um domänenspezifische Sprachen über Makros zu erstellen.
quelle
Hier sind einige Beispiele:
Planen:
define
für Funktionsdefinitionen. Grundsätzlich ist es ein kürzerer Weg, eine Funktion zu definieren.let
zum Erstellen von Variablen mit lexikalischem Gültigkeitsbereich.Clojure:
defn
, nach seinen Unterlagen:Entspricht (def name (fn [params *] exprs *)) oder (def name (fn ([params *] exprs *) +)) mit einem beliebigen Dokument-String oder Attribut, das den var-Metadaten hinzugefügt wurde
for
: Listenverständnissedefmacro
: ironisch?defmethod
,defmulti
: Arbeiten mit mehreren Methodenns
Viele dieser Makros erleichtern das Schreiben von Code auf einer abstrakteren Ebene. Ich denke, Makros ähneln in vielerlei Hinsicht der Syntax in Nicht-Lisps.
Die Zeichnungsbibliothek Incanter bietet Makros für einige komplexe Datenmanipulationen.
quelle
Makros sind nützlich, um einige Muster einzubetten.
Zum Beispiel definiert Common Lisp die
while
Schleife nicht, sondern hatdo
eine, mit der sie definiert werden kann.Hier ist ein Beispiel von On Lisp .
Dies gibt "12345678910" aus und wenn Sie versuchen zu sehen, was passiert mit
macroexpand-1
:Dies wird zurückkehren:
Dies ist ein einfaches Makro, aber wie bereits erwähnt, werden sie normalerweise zum Definieren neuer Sprachen oder DSLs verwendet. Anhand dieses einfachen Beispiels können Sie jedoch bereits versuchen, sich vorzustellen, wie Sie damit umgehen können.
Das
loop
Makro ist ein gutes Beispiel dafür, was Makros können.Common Lisp verfügt über eine andere Art von Makros, das sogenannte Reader-Makro, mit dem Sie die Interpretation des Codes durch den Reader ändern können. Sie können also # {und #} verwenden, um Trennzeichen wie # (und #) zu verwenden.
quelle
Folgendes verwende ich zum Debuggen (in Clojure):
Ich musste mich in C ++ mit einer von Hand gerollten Hash-Tabelle auseinandersetzen, bei der die
get
Methode eine nicht konstante Zeichenfolgenreferenz als Argument verwendete, was bedeutet, dass ich sie nicht mit einem Literal aufrufen kann. Um das einfacher zu machen, habe ich Folgendes geschrieben:Es ist zwar unwahrscheinlich, dass dieses Problem auftaucht, aber ich finde es besonders schön, dass Sie Makros haben können, die ihre Argumente nicht zweimal auswerten, indem Sie beispielsweise einen Real einführen Let-Bindung . (Zugegeben, hier hätte ich mich zurechtfinden können).
Ich greife auch auf den furchtbar hässlichen Trick zurück, Sachen in ein Päckchen zu wickeln
do ... while (false)
so etwas dass man sie im damaligen Teil eines If verwenden kann und das übrige Teil immer noch wie erwartet funktioniert. Sie brauchen dies nicht in lisp, das ist eine Funktion von Makros, die auf Syntaxbäumen ausgeführt werden, anstatt von Strings (oder Token-Sequenzen, glaube ich, im Fall von C und C ++), die dann analysiert werden.Es gibt einige eingebaute Threading-Makros, mit denen Sie Ihren Code so reorganisieren können, dass er sauberer gelesen wird ("Threading" wie "Code zusammenfügen", nicht Parallelität). Beispielsweise:
Es nimmt die erste Form an
(range 6)
und macht es zum letzten Argument der nächsten Form,(filter even?)
die wiederum zum letzten Argument der nächsten Form usw. gemacht wird, so dass das Obige umgeschrieben wirdIch denke, das erste liest sich viel klarer: "Nehmen Sie diese Daten, machen Sie das, dann machen Sie das, dann machen Sie das andere und wir sind fertig", aber das ist subjektiv; Objektiv gesehen stimmt es, dass Sie die Operationen in der Reihenfolge lesen, in der sie ausgeführt werden (Faulheit wird ignoriert).
Es gibt auch eine Variante, bei der die vorherige Form als erstes (und nicht als letztes) Argument eingefügt wird. Ein Anwendungsfall ist die Arithmetik:
Liest als "nehmen Sie 17, subtrahieren Sie 2 und teilen Sie durch 3".
Apropos Arithmetik: Sie können ein Makro schreiben, das das Parsen von Infixnotationen ausführt, so dass Sie beispielsweise sagen können, dass
(infix (17 - 2) / 3)
es ausspuckt,(/ (- 17 2) 3)
was den Nachteil hat, dass es weniger lesbar ist und den Vorteil hat, ein gültiger Lisp-Ausdruck zu sein. Das ist der Teil der DSL / Daten-Subsprache.quelle