2014-06-22 5 views
5

Ich habe ein einfaches Makro:Wie richtig in der `defmacro` Syntax-quote und unquote verwenden

(defmacro macrotest [coll] 
    `(let [result# ~(reduce + coll)] 
     result#)) 

Warum, wenn dieser Code funktioniert:

(macrotest [1 2 3]) 

nicht diesen Code Arbeit ?

(def mycoll [1 2 3]) 
(macrotest mycoll) 
+3

Der reduzieren wird bei der Kompilierung geschieht, so dass Sie zu tun versuchen (+ somesymbol reduzieren), wenn Sie es auf mycoll nennen. Was genau willst du mit deinem Makro? Ich vermute du wolltest (reduziere + ~ coll). –

+3

macroexpand ist dein Freund – dsm

Antwort

10

Symbole sind nicht das gleiche wie der Wert auf sie verweisen.

Einer der wichtigen Überlegungen zu Makros ist nicht nur, wie sie neuen Code zu erstellen, sondern auch, wie sie die Argumente behandeln Sie in geben

Bedenken Sie:.

(def v [1 2 3])

(somefunction v)

Das Argument v, das an diese Funktion übergeben wird, kommt nicht innerhalb der Funktion als Symbol, v, an . Da dies ein Funktionsaufruf ist, werden die Argumente zuerst ausgewertet, und dann wird der resultierende Wert in die Funktion übergeben. So wird die Funktion [1 2 3] sehen.

Aber Makros sind nicht so, obwohl es leicht zu vergessen ist. Wenn Sie anrufen:

(somemacro v)

v ist nicht ausgewertet und tut nicht Pass in [1 2 3]. Innerhalb des Makros erhalten Sie nur ein Symbol, v. Jetzt kann Ihr Makro das Symbol, das Sie übergeben haben, im Code des Makros ausgeben, aber es kann nichts mit dem Wert von v tun, es sei denn, Sie fügen eine zusätzliche Bewertungsebene hinzu, indem Sie eval verwenden (siehe Fußnote - nicht empfohlen).

Wenn Sie das Makroargument aufheben, erhalten Sie ein Symbol, keinen Wert.

Betrachten Sie dieses Beispiel:

user> (def a 5) 
#'user/a 
user> (defmacro m [x] `(+ ~x ~x)) 
#'user/m 
user> (m a) 
10 
user> (macroexpand '(m a)) 
(clojure.core/+ a a) 
user> (defmacro m [x] `~(+ x x)) 
#'user/m 
user> (m a) 
ClassCastException clojure.lang.Symbol cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:126) 

Dieser wie der Versuch, wäre dies bei der REPL zu tun: (+ 'a 'a) Hinzufügen von Symbolen, nicht die Werte addiert diese Symbole zu zeigen.

Sie könnten diese beiden verschiedenen Definitionen des Makros m betrachten und denken, dass sie dasselbe tun. Aber in der zweiten, gibt es einen Versuch, tun etwas mit dem Symbolx, um damit zu tun. Im ersten Makro wird der Compiler nicht aufgefordert, irgendetwas mit x zu tun. Es wird lediglich aufgefordert, die Additionsoperation auszugeben, die dann zur Laufzeit tatsächlich verarbeitet wird.

Denken Sie daran, dass der Punkt eines Makros ist Code nicht ausführen Laufzeitaktivität, die eine Funktion tun würde.Der Code, der erstellt wird, wird dann sofort ausgeführt (über implizite eval), so dass es leicht ist, diese Trennung zu vergessen, aber sie sind zwei verschiedene Schritte.

Als letzte Übung, täuschen Sie vor, Sie sind ein Compiler. Ich möchte Sie, mir sagen jetzt was ist der resultierende Wert dieses Codes:

(* t w)

Nun? Es ist unmöglich. Es gibt keine Möglichkeit, mir die Antwort auf diese Frage zu geben, weil Sie keine Ahnung haben, was t und w sind. Später, zur Laufzeit, wenn diese vermutlich einen Wert haben, wird es jedoch einfach.

Und das ist was Makros tun: Sie geben Zeug für die Laufzeit aus.


(Sehr viel nicht zu empfehlen, aber weiter zu helfen, beschreiben, was passiert, das funktioniert :)

user> (def a 1)

user> (defmacro m [ x] `~ (+ (eval x) (eval x)))

user> (ma)

Verwandte Themen