4

我创建了一个宏,它创建了一个dispatcher带有 3 个关联函数的命名get-dispatcherset-dispatchercall-dispatcher与调度程序一起工作(他们得到一个调度函数,添加一个或调用一个)。一切正常!但是,现在我想自动创建相关的函数名称,因此我将宏的所有这些内部结构放入let定义该简单构造函数的 a 中。请注意,在下面的代码中,只有get-函数的名称是使用该自动化构造的。set-and one的call-名称创建仍然具有手动气味。

(defmacro create-dispatcher [name]
  ;creates a set of dispatching functions tagged

  `(do
    ;define dispatcher
    (def ~(symbol name) ~(atom {}))

    (let
      [name-w-prefix (fn [x] (~(symbol (str x "-" name))))]
        ; -- define getter
        (defn (name-w-prefix "get")
          "get-dispatcher [tag]: get a dispatcher fn by tag"
          (~'[] (println "no tag is provided for '" ~(str name) "' dispatcher"))
          (~'[tag]
            (do
              (println "dispatcher '" ~(str name) "' called with '" ~'tag "' tag")
              ; return the tagged dispatcher
              ( (keyword ~'tag) @~(symbol name) )))

        )
        ; -- define caller
        (defn ~(symbol (str "call-" name))
          "get-dispatcher [tag & args]: call a dispatcher fn by tag and apply to the args"
          ~'[tag & args]
          (apply (~(symbol (str "get-" name)) ~'tag) ~'args)
          )
        ; -- define setter
        (defn ~(symbol (str "set-" name))
          ~'[tag fn]
          "add-dispatcher [tag fn]: add a dispatcher fn associated with the tag"
          (swap! ~(symbol name) assoc (keyword ~'tag) ~'fn)
          )
     )

    ; -- report
    (println "created dispatcher set for '" ~(str name) "' ok!")
    ))

但是,有一个问题。语句绑定name-w-prefix中的会导致错误。let我该如何解决?

(也欢迎任何关于改进的建议,因为我是新手,这几乎是我在 Clojure 中写的第一件事)

4

1 回答 1

9

宏中的所有符号都在当前命名空间中解析,并期望评估为 var。您可以引用该name-w-prefix符号,但这可能会与宏扩展期间传入宏的符号发生冲突。因此,Clojure 提供了一种特殊的语法,用于以语法引用形式生成符号——只需#在符号末尾附加 a,Clojure 就会将其视为带引号的自动生成符号。因此,在这种情况下,将出现的替换为name-w-prefixwithname-w-prefix#就可以了。

退一步看看你的总体目标是什么,我认为你应该将name-w-prefix定义移到语法引号之外,然后使用 syntax-escape 来调用它。否则,你会得到更多的错误,因为defn需要一个符号,所以一旦扩展宏必须生成一个defn具有符号作为其第二项的表单。类似于以下内容:

(defmacro create-dispatcher [name]
  (let [name-w-prefix #(symbol (str % "-" name))]
    `(do
       (def ~(symbol name) (atom {}))
       (defn ~(name-w-prefix "get")
         ([] (println "no tag provided"))
         ([tag#] (println "called with tag" tag#))))))

请注意,我已根据我上面所说的内容更改~'[tag][tag#]in 。defn

于 2013-02-27T21:06:29.020 回答