1

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

(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
4

3 回答 3

4

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

  • 创建函数的宏 - 好的
  • 创建另一个宏的宏 - 好的
  • 创建宏的函数 - 不正常(不常见且不常见)。

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

您的第一个代码示例适用于 REPL,但不适用于已编译的 JAR。在 REPL 中,您处于 compile-time->run-time->print->loop 中,并且由于循环的原因,您从运行时返回到 compile->time,这就是最后一点在 REPL 中起作用的原因。在已编译的代码中,您将只有编译后的代码,并且只有运行时世界,除非您eval在代码中使用它可以使您回到编译时间....等我头疼,但我希望这能让事情变得清晰:)

于 2013-02-26T17:43:28.037 回答
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
于 2013-02-26T16:35:25.037 回答
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)

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

于 2013-02-26T17:27:00.610 回答