传递给宏时评估arg

时间:2015-02-16 11:05:38

标签: clojure

我有一个小宏,它将一系列cmds作为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删除jive-add-ons; yum install jive-add-ons 1.2”

但目前它返回 -

  

“yum删除jive-add-ons; yum install jive-add-ons   (clojure.core / unquote version)“

2 个答案:

答案 0 :(得分:2)

您需要考虑需要生成宏的表单。例如,在您的情况下,您可能希望宏在扩展后生成类似的内容:

(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))]))

宏转换了String列表中的前3个元素,但没有触及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可以帮助你发展你的宏技能。

答案 1 :(得分:1)

(defmacro cmd [& cmds]
  `(join "; "
     [~@(for [cmd cmds]
         `(join " "
            [~@(for [arg cmd]
                 (if (and (seq? arg) (= (first arg) 'clojure.core/unquote))
                   (second arg)
                   (list 'quote arg)))]))]))

如果您与原始代码进行比较,您会看到这一个延迟调用加入到运行时,并将非引号形式作为结果命令模板中的表达式。