Deseo enviar var-args de una función a una macro, aún como var-args. Aquí está mi código:Cómo expandir una secuencia (var-args) en elementos distintos
(defmacro test-macro
[& args]
`(println (str "count=" ~(count args) "; args=" [email protected])))
(defn test-fn-calling-macro
[& args]
(test-macro args))
La salida de (test-macro "a" "b" "c")
es lo que quiero: count=3; args=abc
La salida de (test-fn-calling-macro "a" "b" "c")
es: count=1; args=("a" "b" "c")
, ya que args se envía como un solo argumento a la macro. ¿Cómo puedo expandir estos argumentos en mi función para llamar a la macro con los 3 argumentos?
Supongo que me está perdiendo una simple función central, pero no puedo encontrarla. Gracias
EDIT 2 - Mi código "real", que se muestra en la sección Editar Esto no es una situación válida para utilizar esta técnica.
Como ha señalado @ Brian, la macro xml-to-cass
puede ser sustituida por una función como esta:
(defn xml-to-cass
[zipper table key attr & path]
(doseq [v (apply zf/xml-> zipper path)] (cass/set-attr! table key attr v)))
EDITAR - la siguiente sección va más allá de mi pregunta original, pero alguna idea es bienvenida
El código anterior es simplemente el más simple que podría venir para identificar mi problema. Mi código real trata de clj-cassandra y zip-filter. También puede parecer sobre ingeniería, pero es solo un proyecto de juguete y estoy tratando de aprender el idioma al mismo tiempo.
Quiero analizar un XML encontrado en mlb.com e insertar valores encontrados en una base de datos de cassandra. Aquí está mi código y el pensamiento detrás de él.
Paso 1 - Función que funciona bien, pero contiene la duplicación de código
(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))))
El parámetro a import-game-xml-1
puede ser por ejemplo "gid_2010_05_01_colmlb_sfnmlb_1/"
. Elimino el "gid_" y la barra al final para convertirlo en la clave de los juegos ColumnFamily en mi base de datos.
Encontré que el 3 doseq
era mucha duplicación (y debería haber más de 3 en la versión final). Así que el código de plantillas usando una macro parecía apropiado aquí (corríjanme si me equivoco).
Paso 2 - Presentación de una macro para crear plantillas de código (todavía funciona)
(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))))
Creo que es una mejora, pero todavía veo cierta duplicación de siempre reutilizando los mismos 3 parámetros en mis llamadas a xml-to-cass
. Es ahí donde introduje una función intermedia para cuidar de ellos.
Paso 3 - Adición de una función para llamar a la macro (el problema es aquí)
(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
Muchas gracias por tomarse el tiempo para escribir eso. Me siento como iluminado ahora. También revisaré las publicaciones de tu blog. Lástima que no aprendí Lisp cuando era un niño en lugar de un adulto obstinado. Me cuesta mucho entender mi cerebro. Pero hasta ahora vale la pena el dolor. No me rendiré y espero comprender el pensamiento. – Damien
¡Perdón por responder en kata! Hay sutilezas, y uno tiene que jugar por un tiempo antes de entender. No te preocupes por tu cerebro No toqué ninguna réplica hasta que cumplí 35 años. Me lleva alrededor de un año. Algún día intentarás escribir algo en un idioma con sintaxis y de pronto te darás cuenta de que te sientes atrapado por él. –
La manera de asimilarlo realmente es pasar por el SICP y hacer todos los ejercicios. http://mitpress.mit.edu/sicp/ De hecho, apenas menciona las macros, pero sobre el momento en que escribe un intérprete de esquema en el esquema, comprenderá exactamente lo que está sucediendo en el evaluador, y en ese punto las macros son un corolario trivial. Incluso si su objetivo es programar en clojure u otro lisp complejo, esto es algo bueno que hacer, porque el esquema es tan simple que puede ver las ideas. Un poco como aprender Java primero si lo que realmente quieres hacer es C++. –