Ich bin neu in Clojure, ich bin neu in Makros und ich habe keinen vorherigen Hintergrund in Lisp. Ich fuhr fort, mein eigenes Switch-Case-ähnliches Formular zu erstellen, und endete mit folgendem:
(defmacro switch-case [v cases default] (if (cases v) (cases v) default ))
und dann versucht, eine Funktion zu machen und endete mit diesem:
(defn fn-switch-case [v cases default] (if (cases v) (cases v) default ))
Beide
(switch-case 5 {6 "six" 7 "seven"} "not there")
und
(fn-switch-case 5 {6 "six" 7 "seven"} "not there")
Funktioniert gut.
Was könnte das Szenario sein, in dem ich ein Makro benötige und eine Funktion nicht funktioniert? Gibt es einen Nachteil in meinen Funktions- oder Makroimplementierungen?
Antworten:
Ein Vorteil von Makros besteht darin, dass sie nicht den gleichen Bewertungsregeln wie Funktionen folgen, da sie nur Transformationen für den Code durchführen.
Ein Beispiel für ein Konstrukt, das Sie nicht nur mit Funktionen erstellen können, ist das Threading- Makro von Clojure .
Sie können einen Ausdruck als erstes Argument in eine Folge von Formularen einfügen:
ist äquivalent zu
Sie könnten diese Art von Konstrukt nicht nur mit Funktionen erstellen, da vor der
->
Auswertung des Ausdrucks die innere(+ 3)
und(* 4)
die Bedeutung->
erhalten würdeund es muss die tatsächlichen Funktionen sehen, die verwendet werden, um zu arbeiten.
Überlegen Sie in Ihrem Beispiel, ob das Ergebnis in einigen Fällen Nebenwirkungen haben sollte, oder rufen Sie eine andere Funktion auf. Eine Funktionsversion kann nicht verhindern, dass dieser Code in einer Situation ausgeführt wird, in der dieses Ergebnis nicht ausgewählt wird, wohingegen ein Makro verhindern könnte, dass dieser Code ausgewertet wird, es sei denn, es ist die gewählte Option.
quelle
Ein lustiger Aspekt von Makros ist, dass sie Ihnen die Möglichkeit geben, die Syntax Ihres Lisp zu erweitern und neue syntaktische Funktionen hinzuzufügen. Dies geschieht nur, weil an ein Makro übergebene Argumente nur zur Laufzeit und nicht zum Zeitpunkt von ausgewertet werden Kompilieren Sie Ihr Makro. Als Beispiel
-> ->>
bewerten die ersten / letzten Thread-Makros in clojure ihre Ausdrucksargumente nicht, andernfalls könnten diese ausgewerteten Ergebnisse nichts mehr akzeptieren (sagen wir(+ 3) => 3
und3
ist keine Funktion, die Ihr erstes Hauptargument in so etwas akzeptieren könnte(-> 4 (+ 3))
).Wenn mir diese Syntax gefällt und ich sie meiner Common Lisp-Implementierung hinzufügen möchte (die sie nicht hat), kann ich sie addieren, indem ich selbst ein Makro definiere. Etwas wie das:
Jetzt könnte ich sie in Common Lisp genauso verwenden wie in Clojure:
Vielleicht möchten Sie auch eine neue Syntax für die
range
Funktion von clojure mit Ihren eigenen Schlüsselwörtern für Ihre Syntax haben, etwa:kann wie folgt definiert werden:
oder fügen Sie etwas hinzu, das Common Lisp ähnelt (nur zum Beispiel, da all dies natürlich bereits in den Sprachen möglich ist!):
quelle
Ich bin mir nicht ganz sicher, was Ihre Switch-Case-Funktion tun soll. Was sollte der Wert sein von:
Dies kann hilfreich sein, wenn Sie die Quelle der zugehörigen Kernfunktionen / Makros mithilfe von repl angezeigt haben
Im Allgemeinen ist ein Grund, warum Sie möglicherweise ein Makro im Gegensatz zu einer Funktion wünschen, die Verwendung des
case
Kernmakroswird nicht hallo drucken. Wenn
case
es jedoch als Funktion definiert wäre, würde "Hallo" auf dem Bildschirm ausgegeben und der gesamte Ausdruck würde ausgewertet"not there"
. Dies liegt daran, dass Funktionen ihre Argumente vor jedem Aufruf der Funktion auswerten.quelle