我有一个通用转换库的以下代码:
(defn using-format [format] {:format format})
(defn- parse-date [str format]
(.parse (java.text.SimpleDateFormat. format) str))
(defn string-to-date
([str]
(string-to-date str (using-format "yyyy-MM-dd")))
([str conversion-params]
(parse-date str (:format (merge (using-format "yyyy-MM-dd") conversion-params)))))
我需要能够这样称呼它:
(string-to-date "2011-02-17")
(string-to-date "2/17/2011" (using-format "M/d/yyyy"))
(string-to-date "2/17/2011" {})
第三种情况有点问题: map 不一定包含键
:format
这对功能至关重要。这就是为什么merge
带有默认值。我需要有十几个类似的函数来在所有其他类型之间进行转换。有没有更优雅的方式不需要我复制粘贴,使用
merge
等等在每个单一的功能?理想情况下,寻找这样的东西(宏?):
(defn string-to-date
(wrap
(fn [str conversion-params]
(parse-date str (:format conversion-params))) ; implementation
{:format "yyyy-MM-dd"})) ; default conversion-params
...这将产生一个重载函数(一元和二元),二进制具有
merge
就像在第一个例子中一样。
最佳答案
因此,为了更严格地定义它,您需要创建一个宏来创建转换器函数。转换器函数是具有两个参数的函数,一个参数和两个参数。转换器函数的第一个参数是要转换的对象。第二个参数是一个选项映射,它会以某种方式影响转换(就像你的例子中的格式字符串。)
可以指定默认参数映射。当使用一个参数调用时,转换器函数将使用默认参数映射。当使用两个参数调用时,转换器函数会将默认参数映射与传入的参数映射合并,以便传入的参数覆盖默认值(如果存在)。
让我们称这个宏为 def-converter。 Def 转换器将接受三个参数,第一个是要创建的函数的名称。第二个是两个参数的匿名函数,它实现了双元转换器,没有默认的参数合并。第三个参数是默认的参数映射。
像这样的事情会起作用:
(defmacro def-converter [converter-name converter-fn default-params]
(defn ~converter-name
([to-convert#]
(let [default-params# ~(eval default-params)]
(~converter-fn to-convert# default-params#)))
([to-convert# params#]
(let [default-params# ~(eval default-params)]
(~converter-fn to-convert# (merge default-params# params#))))))
然后你可以像这样使用它:
(def-converter
string-to-date
(fn [to-convert conversion-params]
(parse-date to-convert conversion-params))
(using-format "yyyy-MM-dd"))
但是您必须对您的辅助功能之一进行更改:
(defn- parse-date [str params]
(.parse (java.text.SimpleDateFormat. (:format params)) str))
这是因为宏需要足够通用来处理参数的任意映射,所以我们不能指望。可能有解决方法,但我想不出比将其推送到辅助函数(或需要传递给 def-converter 的匿名函数)更麻烦的方法。
关于Clojure:从模板生成函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5035086/