357 Stimmen

Was macht Lisp-Makros so besonders?

Lesen Paul Grahams Aufsätze über Programmiersprachen würde man meinen, dass Lisp-Makros sind der einzig richtige Weg. Als vielbeschäftigter Entwickler, der auf anderen Plattformen arbeitet, hatte ich bisher nicht das Privileg, Lisp-Makros zu verwenden. Bitte erklären Sie mir, was diese Funktion so leistungsfähig macht, denn ich möchte sie verstehen.

Bitte beziehen Sie dies auch auf etwas, das ich aus der Welt der Python-, Java-, C#- oder C-Entwicklung verstehen würde.

12voto

JacquesB Punkte 40790

Ein Lisp-Makro nimmt ein Programmfragment als Eingabe. Dieses Programmfragment stellt eine Datenstruktur dar, die beliebig manipuliert und transformiert werden kann. Am Ende gibt das Makro ein anderes Programmfragment aus, und dieses Fragment wird zur Laufzeit ausgeführt.

C# verfügt nicht über eine Makrofunktion, aber ein Äquivalent wäre, wenn der Compiler den Code in einen CodeDOM-Baum parst und diesen an eine Methode weitergibt, die diesen in einen anderen CodeDOM umwandelt, der dann in IL kompiliert wird.

Dies könnte verwendet werden, um eine "Zucker"-Syntax wie die for each -Erklärung using -Klausel, linq select -Ausdrücke usw. als Makros, die in den zugrunde liegenden Code umgewandelt werden.

Wenn Java Makros hätte, könnte man die Linq-Syntax in Java implementieren, ohne dass Sun die Basissprache ändern müsste.

Hier ist Pseudocode für ein Lisp-ähnliches Makro in C# zur Implementierung von using aussehen könnte:

define macro "using":
    using ($type $varname = $expression) $block
into:
    $type $varname;
    try {
       $varname = $expression;
       $block;
    } finally {
       $varname.Dispose();
    }

12voto

Matt Curtis Punkte 22328

Stellen Sie sich vor, was Sie in C oder C++ mit Makros und Vorlagen machen können. Sie sind sehr nützliche Hilfsmittel für die Verwaltung von sich wiederholendem Code, aber sie sind auf ziemlich gravierende Weise eingeschränkt.

  • Die begrenzte Makro-/Template-Syntax schränkt ihre Verwendung ein. Sie können zum Beispiel keine Schablone schreiben, die sich zu etwas anderem als einer Klasse oder einer Funktion erweitert. Makros und Vorlagen können interne Daten nicht einfach verwalten.
  • Die komplexe, sehr unregelmäßige Syntax von C und C++ macht es schwierig, sehr allgemeine Makros zu schreiben.

Lisp und Lisp-Makros lösen diese Probleme.

  • Lisp-Makros werden in Lisp geschrieben. Sie haben die volle Macht von Lisp, um das Makro zu schreiben.
  • Lisp hat eine sehr regelmäßige Syntax.

Sprechen Sie mit jemandem, der C++ beherrscht, und fragen Sie ihn, wie viel Zeit er damit verbracht hat, all die Schablonen zu lernen, die er für die Schablonen-Metaprogrammierung benötigt. Oder all die verrückten Tricks in (ausgezeichneten) Büchern wie Modernes C++-Design die immer noch schwer zu debuggen und (in der Praxis) nicht zwischen realen Compilern portierbar sind, obwohl die Sprache seit einem Jahrzehnt standardisiert ist. All das fällt weg, wenn die Sprache, die Sie für die Metaprogrammierung verwenden, dieselbe ist, die Sie zum Programmieren benutzen!

12voto

ZakW Punkte 826

Da die vorhandenen Antworten gute konkrete Beispiele enthalten, die erklären, was Makros bewirken und wie sie funktionieren, wäre es vielleicht hilfreich, einige der Gedanken darüber zusammenzutragen, warum die Makrofunktion einen bedeutenden Gewinn darstellt im Verhältnis zu anderen Sprachen zunächst aus diesen Antworten, dann aus einer großen von anderswo:

... in C müssten Sie einen eigenen Präprozessor schreiben [was wahrscheinlich als hinreichend kompliziertes C-Programm ] ...

- Vatine

Sprechen Sie mit jemandem, der C++ beherrscht, und fragen Sie ihn, wie viel Zeit er damit verbracht hat, all die Template-Fummeleien zu lernen, die er braucht, um Template-Metaprogrammierung zu betreiben [die immer noch nicht so leistungsfähig ist].

- Matt Curtis

... in Java muss man sich den Weg mit Bytecode Weaving hacken, obwohl einige Frameworks wie AspectJ es erlauben, dies mit einem anderen Ansatz zu tun, ist es im Grunde ein Hack.

- Miguel Ping

DOLIST ist vergleichbar mit foreach in Perl oder for in Python. Java fügte eine ähnliche Art von Schleifenkonstrukt mit der "erweiterten" for-Schleife in Java 1.5 als Teil von JSR-201 hinzu. Beachten Sie, was für einen Unterschied Makros machen. Ein Lisp-Programmierer, dem ein häufiges Muster in seinem Code auffällt, kann ein Makro schreiben, um sich eine Abstraktion dieses Musters auf Quellcodeebene zu verschaffen. Ein Java-Programmierer, dem das gleiche Muster auffällt, muss Sun davon überzeugen, dass diese spezielle Abstraktion es wert ist, der Sprache hinzugefügt zu werden. Dann muss Sun einen JSR veröffentlichen und eine branchenweite "Expertengruppe" einberufen, um alles zu klären. Dieser Prozess dauert nach Angaben von Sun durchschnittlich 18 Monate. Danach müssen alle Compiler-Autoren ihre Compiler aktualisieren, um die neue Funktion zu unterstützen. Und selbst wenn der Lieblingscompiler des Java-Programmierers die neue Java-Version unterstützt, kann er die neue Funktion wahrscheinlich "immer noch" nicht nutzen, bis er die Quellkompatibilität mit älteren Java-Versionen aufheben darf. Ein Ärgernis, das Common Lisp-Programmierer innerhalb von fünf Minuten selbst beheben können, plagt Java-Programmierer also jahrelang.

- Peter Seibel, in "Praktisches Common Lisp"

11voto

Miguel Ping Punkte 17786

Ich bin mir nicht sicher, ob ich den (hervorragenden) Beiträgen der anderen etwas hinzufügen kann, aber...

Lisp-Makros funktionieren aufgrund der Natur der Lisp-Syntax hervorragend.

Lisp ist ein sehr regelmäßig Sprache (denken Sie an alles ist ein Liste ); Makros ermöglichen es Ihnen, Daten und Code als dasselbe zu behandeln (kein String-Parsing oder andere Hacks sind erforderlich, um Lisp-Ausdrücke zu ändern). Kombiniert man diese beiden Funktionen, so erhält man eine sehr sauber Art und Weise, den Code zu ändern.

Edita: Was ich damit sagen wollte, ist, dass Lisp homoikonisch Das bedeutet, dass die Datenstruktur für ein Lisp-Programm in Lisp selbst geschrieben wird.

Am Ende hat man also eine Möglichkeit, seinen eigenen Code-Generator auf der Sprache aufzubauen, indem man die Sprache selbst mit all ihren Möglichkeiten nutzt (z. B. muss man sich in Java mit Bytecode-Weaving durchschlagen, obwohl einige Frameworks wie AspectJ dies mit einem anderen Ansatz ermöglichen, ist es im Grunde ein Hack).

In der Praxis müssen Sie mit Makros Ihre eigenen Mini-Sprache auf Lisp aufsetzen, ohne dass zusätzliche Sprachen oder Werkzeuge erlernt werden müssen, und dabei die volle Leistungsfähigkeit der Sprache selbst nutzen.

5voto

dmitry_vk Punkte 4291

Kurz gesagt, Makros sind Transformationen von Code. Sie ermöglichen die Einführung vieler neuer Syntaxkonstanten""

E

(iter (for (id name) in-clsql-query "select id, name from users" on-database *users-database*)
      (format t "User with ID of ~A has name ~A.~%" id name))

a

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X