gpt4 book ai didi

clojure - 如何将序列 (var-args) 扩展为不同的项

转载 作者:行者123 更新时间:2023-12-02 15:20:58 25 4
gpt4 key购买 nike

我想将函数的 var-args 发送到宏,仍然作为 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")因为 args 作为单个参数发送给宏。如何在函数中扩展此参数以便使用 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-filter。它也可能看起来过度设计,但这只是一个玩具项目,我同时尝试学习该语言。

我想解析 mlb.com 上找到的一些 XML 并将找到的值插入到 cassandra 数据库中。这是我的代码及其背后的想法。

第 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))))

我相信这是一个改进,但我仍然发现在调用 xml-to-cass 时总是重复使用相同的 3 个参数,从而导致一些重复。 。那就是我引入了一个中间函数来处理这些问题。

第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

最佳答案

这是一些可能具有启发性的简单代码。

宏与代码生成有关。如果您希望在运行时发生这种情况,出于某种原因,那么您必须在运行时构建和评估代码。这是一项强大的技术。

(defmacro test-macro
[& args]
`(println (str "count=" ~(count args) "; args=" ~@args)))

(defn test-fn-calling-macro
[& args]
(test-macro args))

(defn test-fn-expanding-macro-at-runtime
[& args]
(eval (cons `test-macro args)))

(defmacro test-macro-expanding-macro-at-compile-time
[& args]
(cons `test-macro args))

;; using the splicing notation

(defmacro test-macro-expanding-macro-at-compile-time-2
[& args]
`(test-macro ~@args))

(defn test-fn-expanding-macro-at-runtime-2
[& args]
(eval `(test-macro ~@args)))



(test-macro "a" "b" "c") ;; count=3; args=abc nil
(test-fn-calling-macro "a" "b" "c") ;; count=1; args=("a" "b" "c") nil

(test-fn-expanding-macro-at-runtime "a" "b" "c") ; count=3; args=abc nil
(test-macro-expanding-macro-at-compile-time "a" "b" "c") ; count=3; args=abc nil
(test-macro-expanding-macro-at-compile-time-2 "a" "b" "c") ; count=3; args=abc nil
(test-fn-expanding-macro-at-runtime "a" "b" "c") ; count=3; args=abc nil

如果上述内容没有提供启发,我可以推荐几篇我自己的博客文章吗?

在这一篇中,我从头开始介绍宏,以及 clojure 特别是如何工作的:

http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html

在这一篇中,我将展示为什么运行时代码生成可能有用:

http://www.learningclojure.com/2010/09/clojure-faster-than-machine-code.html

关于clojure - 如何将序列 (var-args) 扩展为不同的项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4675757/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com