我有许多名称空间,每个名称空间都包含一个具有相同名称的函数,例如:
(ns myns.resourceX
...)
(defn create
(println "resourceX/create"))
(ns test)
(myns.resourceX/create)
(myns.resourceY/create)
(您可以想象有resourceX
,resourceY
,resourceZ
等。实际的create
函数最终会发送HTTP POST并返回响应,但这不是这里很重要。)
现在,在另一个命名空间中,我想定义一个带有两个参数的函数:一个资源名称数组(即命名空间名称)和函数名称,例如:
(defn do-verb
[verb res-type]
(??))
所以我可以写:
(do-verb :create :resourceX)
效果如下:
(myns.resourceX/create)
我尝试过的一件事是使用ns-resolve
,例如:
(defn do-verb
[verb res-type & params]
(apply (ns-resolve
(symbol (clojure.string/join ["myns." (name res-type)]))
(symbol (name verb)))
params))
但我不确定使用ns-resolve
- 看起来像是黑客。
我探索过的另一种可能性是定义一个将符号与函数相关联的映射:
(def convert-fns
{:resourceX {:create resourceX/create}
:resourceY {:create resourceY/create}
...})
(defn do-verb [verb res-type & params]
(apply (get-in convert-fns [res-type verb]) params))
但是,对于我来说,每次添加新资源时都需要修改convert-fns
,这有不利之处。
有没有替代方法?
答案 0 :(得分:4)
如果您真的想在命名空间中进行动态查找,可以使用ns-publics
函数。
请参阅以下代码段:
(defn do-verb [verb res-type & params]
(let [ns-symbol (symbol (str "myns." (name res-type)))
publics (ns-publics ns-symbol)
;; Looking up function we need
func (publics (symbol (name verb)))]
(apply func params)))
然而,这仍然看起来有点hacky,与使用ns-resolve
相同。
因此,我建议您将do-verb
函数设为多方法。
(defmulti do-verb (fn [verb res-type & args] [verb res-type]))
之后,为您的每个do-verb
和资源类型实施verb
:
(defmethod do-verb [:create :resourceX] [_ _ & args] ...)
并按如下方式调用:
(do-verb :create :resourceX ...)
正如您所见,签名完全匹配。
答案 1 :(得分:1)
宏应该能够做到这一点,
但要非常小心以下代码 - 我以前从未写过(明智的)宏,所以虽然这似乎有用,但它没有经过测试,而且可能会有问题:
另请注意"用户。"是硬编码的。
sapply(dat, FUN = function(x) {x[substr(x, 1, 1) != substr(x, 3, 3)] <- NA; x})