clojure中函数的重新定义

clojure中函数的重新定义,clojure,Clojure,我想在启动时根据这些函数的元数据重新定义程序中的一些函数 我是clojure的新手,所以我想知道实现这一点的惯用方法是什么 我想做的是使用缓存(比如memcache)来缓存某些函数的结果(数据库结果)。类似于memoize或contrib core.cache,但我希望根据定义缓存策略的元数据,对程序的其余部分透明地重新定义原始函数 Java库通常使用注释和代码生成来实现这一点。但我在想,用什么样的方式来实现这一点呢 我在互联网上探索了一些选择,但它们似乎不太令人满意。 绑定不是我想要的,因为它

我想在启动时根据这些函数的元数据重新定义程序中的一些函数

我是clojure的新手,所以我想知道实现这一点的惯用方法是什么

我想做的是使用缓存(比如memcache)来缓存某些函数的结果(数据库结果)。类似于memoize或contrib core.cache,但我希望根据定义缓存策略的元数据,对程序的其余部分透明地重新定义原始函数

Java库通常使用注释和代码生成来实现这一点。但我在想,用什么样的方式来实现这一点呢

我在互联网上探索了一些选择,但它们似乎不太令人满意。 绑定不是我想要的,因为它只在当前线程上工作。 其他选项似乎是使用一些我想要避免的内部java函数,或者 绑定ns并使用eval重新定义函数

我知道我可以用(key(ns publics'foo))在一个名称空间中列出潜在函数,但还没有探索如何列出非公共函数以及如何列出可用的名称空间(当前已加载?)-也许我可以使用名称空间加载挂钩

编辑: 这是我想到的一个小例子。Wrap是一个根据origs元数据执行缓存的函数。示例中没有缓存和元数据,wrap和orig都位于同一名称空间中

(defn orig []
    "OK")
(defn orig2 []
    "RES 2")

(defn wrap [f & args]
    (let [res (apply f args)]
        println "wrap" f args "=" res
        res))

(set! orig (wrap orig))
(set! orig2 (wrap orig2))
在评估最后两个表单orig和orig2后,应重新定义为使用包装版本。 很遗憾,我在REPL中遇到以下错误:


java.lang.IllegalStateException:无法更改/建立:orig与set的根绑定(无源文件:0)

Clojure将函数存储在
vars
中,以简化此类代码操作。您只需更改var的值以引用适当的函数

如果您有一个潜在缓存函数的列表,那么您可以在它们自己的命名空间中定义它们,并有一些代码使用
def
将顶级绑定设置为指向适当的绑定

(defn cache-all ...)
(defn cache-some ...)
(defn cache-none ...)

 (let [policy (get-current-policy)]
     (cond (= policy :all) (def cacher cache-all)
           (= policy :some) (def cacher cache-some)
           ...
           ))

如果您需要根据新输入实际定义函数,则
eval
是一种idomatic方法。

您可以再次使用
def/defn
,它将更改函数的定义(从技术上讲,它将编写一个使用相同名称的新定义)

所以你可以这样做:

(def orig (wrap orig))
(def orig2 (wrap orig2))
另外,如果我理解您的意图:
wrap
应该返回函数,而不是结果

(defn wrap [f]
  (fn [& args]
    (let [res (apply f args)]
      (println "wrap" f args "=" res)
      res)))

如果查看
memoize
标准函数,它的工作方式正是这样。

只需将适当的函数作为变量传递给执行此工作的代码即可

e、 g


通过这种方式,您可以预先决定使用哪种策略,选择适当的函数,并将其传递到期望缓存函数执行正确操作的代码中。然后,代码就可以调用它作为参数接收的函数。

Hi。谢谢你的回答。我尝试过做类似的事情(我编辑了问题),但它不起作用,错误是“…无法更改/建立根绑定…”
set
仅在
绑定
表单的动态范围内工作,因此它只能更改线程绑定,而不能更改根绑定.Thaks。当然,我需要返回一个函数。我现在的问题是在另一个名称空间中更改函数定义。只是尝试重新定义它会抛出一个错误“无法在当前ns之外创建def”,但是如果我尝试将orig移动到Namespace x并执行(defn wrapns[ns f wrapf](binding[ns(find ns ns ns)](def(wrapf)))(wrapns'x x x/orig user/wrap)它不会重新定义原始函数?实际上,我刚刚看到了有关的文档。它实际上非常适合你想要做的事情<代码>(defmacro-wrapns[var-wrapfn]`(alter-var-route#'~var~wrapfn))应该适合您。请注意,现在它是一个宏,以避免在调用函数名的任何地方都需要使用
#“
。示例用法:
(wrapns clojure.core/map wrap)
谢谢,宏运行良好。我想知道为什么如果我尝试将其更改为函数(并删除引号),它在尝试包装函数orig/orig:java.lang.ClassCastException:orig$orig不能转换为clojure.lang.Var时会出现错误?意味着取符号所指的
Var
,而不是它所持有的值。(因此得到的是框而不是框中的值)。如果你想把它作为一个函数,你只需要调用
(wrapns#'clojure.core/map wrap)
,但是我认为没有它会更干净
memoize
并不能完全做到这一点。实际上,它做的更多:它返回一个调用您的函数的函数,但它缓存调用的结果,因此,如果使用给定的(一组)参数再次调用它,它只返回相同的结果,而不实际调用您的函数。非常适合于流程密集型函数,即副作用只发生一次的函数,或调用创建某个对象的实例,后续调用应返回同一实例,而不是新实例的函数。
user=> (defn strategy-1 [input] (+ input 3))
#'user/strategy-1
user=> (defn strategy-2 [input] (- input 3))
#'user/strategy-2
user=> (defn fun-user [fn input] (fn input))
#'user/fun-user
user=> (fun-user strategy-1 5)  
8
user=> (fun-user strategy-2 5)
2