我有一个小宏,它接受一系列 cmd 作为 clojure 表达式并返回它们的字符串表示形式。我在将参数传递给宏时遇到问题 -
(defmacro cmd [& cmds]
(->> (for [v cmds]
(join " " v))
(join "; ")))
(defn install-jive [version]
(cmd
(yum remove jive-add-ons)
(yum install jive-add-ons ~version)))
(install-jive 1.2)
我希望 fn 返回以下内容 -
(install-jive 1.2) => "yum remove jive-add-ons; yum install jive-add-ons 1.2"
但是目前它返回 -
"yum remove jive-add-ons; yum install jive-add-ons (clojure.core/unquote version)"
最佳答案
您需要考虑需要宏生成什么表单。例如,在您的情况下,您可能希望宏在扩展后生成如下所示的内容:
(defn install-jive [version]
(clojure.string/join
";"
[(clojure.string/join " " (list "yum" "remove" "jive-add-ons"))
(clojure.string/join " " (list "yum" "install" "jive-add-ons" version))]))
该宏将列表中的前 3 个元素转换为 String
形式,并且没有触及 version
。这是一个针对一种表单执行此操作的宏:
(defmacro single-cmd [cmd-form]
`(clojure.string/join " " (list ~@(map str (take 3 cmd-form))
~@(drop 3 cmd-form))))
扩展此宏会生成以下内容:
(macroexpand '(single-cmd (yum remove jive-add-ons version)))
=> (clojure.string/join " " (clojure.core/list "yum" "remove" "jive-add-ons" version))
(macroexpand '(single-cmd (yum remove)))
=> (clojure.string/join " " (clojure.core/list "yum" "remove"))
这就是我们需要将每个命令扩展成的内容。现在我们需要创建一个宏来扩展它:
(cmd
(yum remove jive-add-ons)
(yum install jive-add-ons version))
改成我们想要的形式。这是一个简单的版本,只是为了给您提供想法:
(defmacro cmd [& cmds]
`(clojure.string/join
";"
(list (single-cmd ~(first cmds))
(single-cmd ~(second cmds)))))
展开它会生成:
(macroexpand '(cmd
(yum remove jive-add-ons)
(yum install jive-add-ons version)))
=> (clojure.string/join
";"
(clojure.core/list (user/single-cmd (yum remove jive-add-ons))
(user/single-cmd (yum install jive-add-ons version))))
现在:
(install-jive 1.2)
=> "yum remove jive-add-ons;yum install jive-add-ons 1.2"
我的版本做了两个大假设:前三个元素应始终转换为String
,并且您始终使用两个元素调用cmd
,并且只有两个,命令。您可能需要改进它才能解决您的问题。
但我认为这会插入你走向正确的方向。有一本很棒的书Mastering Clojure Macros这可以帮助您培养宏技能。
关于clojure - 传递给宏时评估 arg,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28539980/