Я хочу отправить var-аргументы функции в макрос, все еще как var-args.
Вот мой код:
(defmacro test-macro
[& args]
`(println (str "count=" ~(count args) "; args=" ~@args)))
(defn test-fn-calling-macro
[& args]
(test-macro args))
Вывод (test-macro "a" "b" "c")
- это то, что я хочу: count=3; args=abc
Вывод (test-fn-calling-macro "a" "b" "c")
: count=1; args=("a" "b" "c")
, потому что аргументы отправляются макросу как один аргумент. Как я могу расширить эти аргументы в моей функции для вызова макроса с 3 аргументами?
Полагаю, мне просто не хватает простой основной функции, но я не могу ее найти. Спасибо
РЕДАКТИРОВАТЬ 2 - Мой «реальный» код, показанный в разделе РЕДАКТИРОВАНИЯ ниже, не является допустимой ситуацией для использования этой техники.
Как указал @Brian, макрос xml-to-cass
можно заменить на такую функцию:
(defn xml-to-cass
[zipper table key attr & path]
(doseq [v (apply zf/xml-> zipper path)] (cass/set-attr! table key attr v)))
РЕДАКТИРОВАТЬ - следующий раздел выходит за рамки моего первоначального вопроса, но любое понимание приветствуется
Приведенный выше код является просто самым простым, с которым я могу прийти, чтобы точно определить мою проблему. Мой реальный код касается clj-cassandra и zip-фильтра. Это также может показаться излишним, но это всего лишь игрушечный проект, и я одновременно пытаюсь выучить язык.
Я хочу проанализировать некоторые XML, найденные на mlb.com, и вставить значения, найденные в базу данных кассандры. Вот мой код и мысли о нем.
Шаг 1 - Функция, которая работает нормально, но содержит дублирование кода
(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))))
Параметр import-game-xml-1
может быть, например, "gid_2010_05_01_colmlb_sfnmlb_1/"
. Я удаляю "gid_" и завершающий слеш, чтобы сделать его ключом игр ColumnFamily в моей базе данных.
Я обнаружил, что 3 doseq
было много дублирования (и в финальной версии их должно быть больше 3). Таким образом, шаблонирование кода с использованием макроса показалось здесь уместным (поправьте меня, если я ошибаюсь).
Шаг 2 - Представление макроса для шаблонов кода (все еще работает)
(defmacro xml-to-cass
[zipper table key attr & path]
`(doseq [v# (zf/xml-> ~zipper ~@path)] (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))))
Я считаю, что это улучшение, но я все еще вижу некоторое дублирование в том, чтобы всегда повторно использовать одни и те же 3 параметра в моих вызовах к xml-to-cass
. Для этого я ввел промежуточную функцию.
Шаг 3 - Добавление функции для вызова макроса (проблема здесь)
(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