我有一个应用程序,其中我有一个函数可以在一些不同的上下文中调用,我希望它调用的一些函数在这些不同的上下文中以不同的方式运行。因此,举个例子,我可能有一些代码,这些代码的功能如下
(defn foo [a context]
(-> a
inc
(#(bar % context))))
(defn bar [a context]
(cond (= context 1) (* a 2)
(= context 2) (/ a 2)))尽管有很多不同的函数,比如bar,但是这些函数都隐藏在自己不关心“上下文”的其他函数中。
因为它们隐藏在一堆其他函数中(因为我为一个上下文编写了这段代码,现在正在添加其他函数),修改所有这些函数以将一个标志传递给所有相关的' bar 's是一个麻烦。我也不太喜欢它作为解决方案。理想情况下,我希望在每个上下文中隐式地使用bar函数的正确版本。
协议也许能解决这个问题。但是,我认为用协议重写我的函数将是一个巨大的麻烦,因为(在我的实际函数中)一些'bar‘函数既使用上下文,又将其传递给使用它的其他函数。因此,我想我必须复制一些代码(或者有不同的协议传递标志)。
我想出的解决方案是为foo创建一个名称空间,然后为每个上下文创建一个单独的名称空间。然后,在每个上下文的名称空间中,我定义了一个单独的名称空间。我更改foo,以便它调用驻留在调用命名空间中的bar函数的版本。这就是:
(ns main)
(defn foo [a]
(-> inc
('bar (ns-map *ns*))))
(ns context-1
(use main))
(defn bar [a]
(* a 2))
(ns context-2
(use main))
(defn bar [a]
(/ a 2))然后,当我从上下文-1命名空间调用foo时,它按预期工作。上下文-2命名空间也是如此。
这基本上是可行的,只是因为我想从不同的名称空间调用上下文1 foo和上下文2 foo,所以我需要为我为foo函数调用进入名称空间的每个名称空间编写一个包装器,然后切换回我开始使用的名称空间。所以,在上下文中-1 ns,我写了这样的东西:
(defn context-1-foo [a]
(let [base-ns *ns*]
(in-ns 'context-1)
(let [result (foo a)]
(in-ns (ns-name base-ns))
result)))这是可行的,而且它不需要太多的改变,但我认为它一定是非惯用的。它似乎也可能是一个邀请,有奇怪的bug。
做这件事的惯用方法是什么?类似地,是否有一种方法可以对代码进行很少的更改?
发布于 2017-02-21 21:42:21
更新:
抱歉,在重新阅读了这个问题之后,我发现这并不能解决你对上下文的要求。查看动态绑定可能会有更好的运气,您可以创建一个宏,如下所示:
(with-context 1
(bar 4))
;; => 8
(with-context 2
(bar 4))
;; => 2原始答案
处理此场景的惯用方法是使用Clojure的多方法。如果要调用的方法取决于上下文中的内容,那么多方法将允许您分派匹配特定上下文的方法,这是未来的证明,因为您可以通过简单地指定匹配的上下文来添加更多的方法。
以你为例:
; you could define the context object like:
{:method :bar-1}
; or
{:method :bar-2}
;; Create multimethod that accepts the 2 params
;; and dispatches on :method in context
(defmulti bar (fn [a context] (:method context)))
;; Method that dispatches when context is {:method :bar-1}
(defmethod bar :bar-1 [a context]
(* a 2))
;; Method that dispatches when context is {:method :bar-2}
(defmethod bar :bar-2 [a context]
(/ a 2))
;; Method that dispatches when context is {:method :bar-3}
(defmethod bar :bar-3 [a context]
; Some third implementation
)然后,只需使用正确的上下文对象调用bar。
(bar 4 {:method :bar-1})
;; => 8
(bar 4 {:method :bar-2})
;; => 2
(bar 4 {:method :bar-3})
;; => nil如上文所示,任何以后的bar实现都可以通过简单的defmethod添加。
https://stackoverflow.com/questions/42378115
复制相似问题