【问题标题】:Using a Clojure macro many times to generate functions多次使用 Clojure 宏生成函数
【发布时间】:2014-02-16 22:15:54
【问题描述】:

我在这里有一个我无法弄清楚的两难境地。我正在尝试创建一堆非常相似的函数,除了几件事(包括它们采用的参数数量)。

我编写了一个宏来创建其中一个似乎可以正常工作的函数。这是宏

(defmacro gen-fn
  [meth args transform-fns]
  `(fn [~'conn ~@args]
     (->> (. metadata-service
            ~meth
            (sec/single-use-ticket ~'conn)
            ~@args)
          ~@transform-fns)))

所以我可以创建两个函数

(def get-source (gen-fn getSource [version source] [bean]))
(def get-all-sources (gen-fn getAllSources [version] [(map bean)]))

当我这样称呼它们时,它们都可以正常工作:

(get-source conn "2013AB" "WHO97")
(get-all-sources conn "2013AB")

现在我有大约 600 个这样的函数要创建,所以如果我能稍微简化一下就好了(最终可能在应用程序启动时从外部源读取它)。所以我在这里的第一个想法是构建一个这样的地图:

(def metadata-methods
  { "getSources" [["version" "source"] ["bean"]]
    "getAllSources" [["version"] ["(map bean)"]] })

或者类似的东西然后使用doseq或者类似的东西来创建函数

(doseq [[function-label [args transform-fns]] metadata-methods]
  (intern *ns* (symbol (->kebab-case function-label))
               (gen-fn function-label [version source] [bean])))

当我运行它时,它似乎可以工作,但调用 (get-source conn "2013AB" "WHO97") 会引发异常,提示类 Proxy 没有匹配的方法“function_label”...

所以不知何故,宏没有正确创建函数。

所以我的问题是 1)有没有一种简单的方法来完成这项工作? 2)我是否在做一些比它需要的更复杂的事情?有没有更简单的方法来完成同样的事情?

除了要生成的每个函数都采用不同数量的参数这一事实之外,普通函数也可以工作,我真的希望每个函数都具有固定的数量。

【问题讨论】:

标签: macros clojure


【解决方案1】:

宏作为参数传递给宏调用中的实际参数表达式,所以在这个调用中:

(gen-fn function-label [version source] [bean])

gen-fn 将传递实际符号 function-label、两个符号的向量和一个符号的向量作为参数。这就是为什么 get-source 不能与 doseq 方法一起使用的原因。

完成这类事情的常用方法是定义一个宏,如您的 gen-fn 和另一个宏,例如 def-fns(遵循使用原始宏名称的复数版本并更改 gen 的通常模式到def,因为我们正在创建变量),发出多个包裹在do 中的gen-fn 表单:

(defmacro def-fns [& args]
  `(do ~@(for [[meth args transform-fns] (partition 3 args)
               :let [name (symbol (->kebab-case (str meth)))]]
           `(def ~name (gen-fn ~meth ~args ~transform-fns)))))

然后说

(def-fns
  getSource [version source] [bean]
  getAllSources [version] [(map bean)])

如果您更愿意使用地图,这也是可能的:

;; def the map first
(defmacro def-fns []
  `(do ~@(for [[meth [args transform-fn]] metadata-methods
               :let [name (symbol (->kebab-case meth))]
           `(def ~name (gen-fn ~meth ~args ~transform-fn)))))

请注意,宏函数将使用 metadata-methods 的编译时值(这很好)。

【讨论】:

  • 这太棒了,米哈尔!我很感激。在这个过程中你也教会了我很多东西。
猜你喜欢
  • 2011-12-12
  • 1970-01-01
  • 2011-05-27
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 2012-07-04
相关资源
最近更新 更多