Ich möchte var-args einer Funktion an ein Makro senden, immer noch als var-args. Hier ist mein Code:So erweitern Sie eine Sequenz (var-args) in verschiedene Elemente
(defmacro test-macro
[& args]
`(println (str "count=" ~(count args) "; args=" [email protected])))
(defn test-fn-calling-macro
[& args]
(test-macro args))
Der Ausgang des (test-macro "a" "b" "c")
ist das, was ich will: count=3; args=abc
Die Ausgabe von (test-fn-calling-macro "a" "b" "c")
ist: count=1; args=("a" "b" "c")
da args als ein einziges Argument an das Makro gesendet wird. Wie kann ich diese Argumente in meiner Funktion erweitern, um das Makro mit den 3 Argumenten aufzurufen?
Ich denke, ich vermisse nur eine einfache Kernfunktion, aber ich bin nicht in der Lage, es zu finden. Dank
EDIT 2 - My "echter" Code unten in EDIT Abschnitt dargestellt ist keine gültige Situation, diese Technik zu verwenden.
Wie @ Brian wies darauf hin, das Makro xml-to-cass
kann mit einer Funktion wie folgt ersetzt:
(defn xml-to-cass
[zipper table key attr & path]
(doseq [v (apply zf/xml-> zipper path)] (cass/set-attr! table key attr v)))
EDIT - der folgende Abschnitt geht über meine ursprüngliche Frage aber jede Einsicht ist willkommen
Der obige Code ist nur das einfachste, mit dem ich mein Problem identifizieren konnte. Mein realer Code behandelt clj-cassandra und zip-filter. Es mag auch übertrieben aussehen, aber es ist nur ein Spielzeugprojekt und ich versuche gleichzeitig, die Sprache zu lernen.
Ich möchte einige XML-Dateien auf mlb.com parsen und Werte in eine Cassandra-Datenbank einfügen. Hier ist mein Code und das Denken dahinter.
Schritt 1 - Funktion, der gut arbeitet, aber Code enthält Vervielfältigung
(ns stats.importer
(:require
[clojure.xml :as xml]
[clojure.zip :as zip]
[clojure.contrib.zip-filter.xml :as zf]
[cassandra.client :as cass]))
(def root-url "http://gd2.mlb.com/components/game/mlb/year_2010/month_05/day_01/")
(def games-table (cass/mk-cf-spec "localhost" 9160 "mlb-stats" "games"))
(defn import-game-xml-1
"Import the content of xml into cassandra"
[game-dir]
(let [url (str root-url game-dir "game.xml")
zipper (zip/xml-zip (xml/parse url))
game-id (.substring game-dir 4 (- (.length game-dir) 1))]
(doseq [v (zf/xml-> zipper (zf/attr :type))] (cass/set-attr! games-table game-id :type v))
(doseq [v (zf/xml-> zipper (zf/attr :local_game_time))] (cass/set-attr! games-table game-id :local_game_time v))
(doseq [v (zf/xml-> zipper :team [(zf/attr= :type "home")] (zf/attr :name_full))] (cass/set-attr! games-table game-id :home_team v))))
Der Parameter import-game-xml-1
"gid_2010_05_01_colmlb_sfnmlb_1/"
zum Beispiel sein kann. Ich entferne den "gid_" und den abschließenden Schrägstrich, um ihn zum Schlüssel der ColumnFamily-Spiele in meiner Datenbank zu machen.
Ich fand, dass die 3 doseq
viel Duplizierung waren (und es sollte mehr als 3 in der endgültigen Version sein). So erschien hier ein Templat mit einem Makro als richtig (korrigiere mich, wenn ich falsch liege).
Schritt 2 - ein Makro für die Code-Templating Einführung (funktioniert noch)
(defmacro xml-to-cass
[zipper table key attr & path]
`(doseq [v# (zf/xml-> ~zipper [email protected])] (cass/set-attr! ~table ~key ~attr v#)))
(defn import-game-xml-2
"Import the content of xml into cassandra"
[game-dir]
(let [url (str root-url game-dir "game.xml")
zipper (zip/xml-zip (xml/parse url))
game-id (.substring game-dir 4 (- (.length game-dir) 1))]
(xml-to-cass zipper games-table game-id :type (zf/attr :type))
(xml-to-cass zipper games-table game-id :local_game_time (zf/attr :local_game_time))
(xml-to-cass zipper games-table game-id :home_team :team [(zf/attr= :type "home")] (zf/attr :name_full))))
Ich glaube, dass eine Verbesserung, aber ich immer noch einige Überschneidungen in immer Wiederverwendung der gleichen 3 Parameter in meine Anrufe zu xml-to-cass
sehen. Das ist, wo ich eine Zwischenfunktion eingeführt habe, um auf jene aufzupassen.
Schritt 3 - eine Funktion Hinzufügen des Makro aufzurufen (das Problem ist hier)
(defn import-game-xml-3
"Import the content of xml into cassandra"
[game-dir]
(let [url (str root-url game-dir "game.xml")
zipper (zip/xml-zip (xml/parse url))
game-id (.substring game-dir 4 (- (.length game-dir) 1))
save-game-attr (fn[key path] (xml-to-cass zipper games-table game-id key path))]
(save-game-attr :type (zf/attr :type)) ; works well because path has only one element
(save-game-attr :local_game_time (zf/attr :local_game_time))
(save-game-attr :home :team [(zf/attr= :type "home"] (zf/attr :name_full))))) ; FIXME this final line doesn't work
Vielen Dank, dass Sie sich die Zeit genommen haben, das zu schreiben. Ich fühle mich jetzt irgendwie erleuchtet. Ich werde mit Sicherheit auch Ihre Blogposts durchgehen. Schade, dass ich Lisp nicht als Kind gelernt habe, sondern als sturer Erwachsener. Es fällt mir schwer, meinen Kopf herumzukriegen. Aber bis jetzt ist es den Schmerz wert. Ich werde nicht aufgeben und hoffentlich das Denken begreifen. – Damien
Entschuldigung, in Kata zu antworten! Es gibt Feinheiten, und man muss eine Weile spielen, bevor man es versteht. Mach dir keine Sorgen um dein Gehirn. Ich habe erst nach ungefähr 35 Jahren ein Rezept berührt. Es dauert ungefähr ein Jahr. Eines Tages wirst du versuchen, etwas in einer Sprache mit Syntax zu schreiben, und plötzlich erkennst du, dass du dich darin gefangen fühlst. –
Der Weg, um wirklich zu grok ist es, durch SICP zu gehen und alle Übungen zu machen. http://mitpress.mit.edu/sicp/ In der Tat erwähnt es kaum Makros, aber über die Zeit, wo Sie einen Schema-Interpreter in Schema schreiben, werden Sie genau verstehen, was im Evaluator vor sich geht, und an diesem Punkt sind Makros eine triviale Folge. Selbst wenn Ihr Ziel ist, in clojure oder einem anderen komplexen Lispeln zu programmieren, ist dies eine gute Sache zu tun, weil Schema so einfach ist, dass Sie die Ideen sehen können. Ein bisschen wie Java zu lernen, wenn Sie wirklich C++ wollen. –