创建宏的函数,宏名称作为参数提供

时间:2013-02-26 16:18:09

标签: clojure

我正在尝试定义一个函数,该函数将创建一个宏,并在尝试动态提供宏名称时遇到问题。这里的缩小代码举例说明了我面临的问题:

(defn create-times-macro [n]
  (defmacro thatManyTimes [a]
    `(* ~n ~a)))

(create-times-macro 2)

(thatManyTimes 3) ;; evals to 6

到目前为止一切顺利。现在说我想提供宏的名称作为参数:

(defn create-times-macro [macroName n]
  (defmacro macroName [a]
    `(* ~n ~a)))

(create-times-macro (symbol "multiplyBy") 3)
(multiplyBy 3) ;; fails with unable to resolve symbol multiplyBy
(create-times-macro "multiplyBy" 3)
(multiplyBy 3) ;; same failure

3 个答案:

答案 0 :(得分:4)

您似乎对使用宏感到困惑。

  • 创建功能的宏 - 确定
  • 创建另一个宏的宏 - 确定
  • 创建宏的功能 - 不行(不常见且通常)。

所有这些都表明宏是编译时间的事实,因为函数是运行时的东西。通常的方向是编译时到运行时,最后一点是从运行时到编译时。

您的第一个代码示例适用于REPL,但它不适用于已编译的JAR。在REPL中,您处于编译时 - >运行时 - > print->循环和循环的becoz,您可以从运行时返回编译 - >时间,这就是为什么最后一点在REPL工作。在编译的代码中,你只有编译代码,并且只有运行时世界,除非你在你的代码中使用eval,这可以让你回到编译时间....等我的头痛,但我希望这把事情弄清楚:)

答案 1 :(得分:1)

不确定这是否是最好的方法,但它确实有效:

=> (defmacro create-times-macro [macroName n]
     (let [the-name (symbol macroName)]
       `(defmacro ~the-name [a#]
          (* ~n a#))))
#'user/create-times-macro
=> (create-times-macro "hi" 3)
#'user/hi
=> (hi 4)
12

答案 2 :(得分:1)

在这种情况下你不需要宏。函数在大多数情况下都适用,并且它们具有比宏更多的能力。例如,如果您将其创建为函数

(defn create-times-fn [n]
  (fn [a]
    (* n a)))

你会得到相同的结果

(def three-times (create-times-fn 3))

(three-times 2)
=> 6

(let [three-times (create-times-fn 3)]
  (three-times 2))

你可以把它作为参数传递给其他函数

(map (create-times-fn 2) (range 5))
=> (0 2 4 6 8)

或将其作为结果返回或与其他功能组合。你用宏丢失了所有这些东西。