Art*_*ldt 14
使用defn重新定义函数可能会破坏调用它的函数,如果它们在调用更改时正在运行.它在开发中是可以的,因为你可以在它休息后重新启动.如果您可以控制何时调用您正在更改的功能,那么它就足够安全了.
defn 是一个宏,解决了类似的东西
(def name (fn [args] (code-here)))
Run Code Online (Sandbox Code Playgroud)
所以它创建了一个函数的实例,然后将它放入var的根绑定中.vars是一个可变数据结构,允许每线程值.所以当你调用defn时,它会分配所有线程都会看到的基值.如果另一个线程然后将var更改为指向某个其他函数,它将更改它的副本而不影响任何其他线程.所有旧线程仍然会看到旧副本
当您通过def再次调用(通过defn宏)重新绑定var的根值时,您将更改未设置其自身值的每个线程将看到的值.决定设置自己的值的线程将继续看到它们自己设置的值,而不必担心从它们下面改变的值.
当进行函数调用时,使用具有函数名称的var的当前值,如执行调用的线程所见(这很重要).因此,如果var的值发生变化,则所有将来的调用都将看到新值; 但他们只会看到根绑定或他们自己的线程本地绑定的更改.所以首先是只有根绑定的正常情况:
user=> (defn foo [] 4)
#'user/foo
user=> (defn bar [] (foo))
#'user/bar
user=> (bar)
4
user=> (defn foo [] 6)
#'user/foo
user=> (bar)
6
Run Code Online (Sandbox Code Playgroud)
然后我们运行另一个线程并在该线程中重新定义foo以返回12
user=> (.start (Thread. (fn [] (binding [foo (fn [] 12)] (println (bar))))))
nil
user=> 12
Run Code Online (Sandbox Code Playgroud)
foo的值(由bar看到)在第一个线程(运行repl的线程)中仍然没有变化
user=> (bar)
6
user=>
Run Code Online (Sandbox Code Playgroud)
接下来,我们将从没有本地绑定的线程下更改根绑定的值,并看到函数foo的值在另一个线程中运行的函数的中途改变:
user=> (.start (Thread. (fn [] (println (bar))
(Thread/sleep 20000)
(println (bar)))))
nil
user=> 6 ;foo at the start of the function
user=> (defn foo [] 7) ;in the middle of the 20 seond sleep we redefine foo
#'user/foo
user=> 7 ; the redefined foo is used at the end of the function
Run Code Online (Sandbox Code Playgroud)
如果对foo的更改(间接调用)改变了参数的数量,那么这将是崩溃而不是错误的答案(这可能更好).在这一点上,很明显,如果我们想要使用变量和devn来改变我们的函数,那么需要做一些事情.
您真的可能希望函数不会更改中间调用,因此您可以使用线程本地绑定来保护您的自我,方法是更改新线程中运行的函数以将foo的当前值保存到其线程本地绑定中:
user=> (.start (Thread. (fn [] (binding [foo foo] (println (bar))
(Thread/sleep 20000)
(println (bar))))))
nil
user=> 7
user=> (defn foo [] 9)
#'user/foo
user=> 7
Run Code Online (Sandbox Code Playgroud)
神奇的是在表达式中,(binding [foo foo] (code-that-uses-foo))这可以被读作"将一个线程本地值赋给foo当前值的foo",这样它就会保持一致,直到绑定表单结束并进入从该绑定表单调用的任何内容.
vars足以保存你的功能,并在开发代码时重新定义它们的内容.使用代码在使用vars的已部署系统上快速自动重新定义函数将不太明智.并不是因为vars不是线程安全的,而是因为在这种情况下vars是一个错误的可变结构来保存你的函数.Clojure对于每个用例都有可变的结构,并且在快速自动编辑需要通过事务运行保持一致的函数的情况下,您最好在refs中保存函数.还有哪种语言可以让您选择保留您职能的结构!*
mik*_*era 10
是的,它是线程安全的....但它确实有副作用.因此,根据您的尝试,您可能会得到意想不到的结果.
实质上,对现有函数进行定义将重新绑定命名空间中的相应var.
这意味着:
只要你理解并对此感到满意 - 你应该没问题.
编辑:回应亚瑟的评论,这是一个例子:
; original function
(defn my-func [x] (+ x 3))
; a vector that holds a copy of the original function
(def my-func-vector [my-func])
; testing it works
(my-func 2)
=> 5
((my-func-vector 0) 2)
=> 5
; now redefine the function
(defn my-func [x] (+ x 10))
; direct call to my-func uses the new version, but the vector still contains the old version....
(my-func 2)
=> 12
((my-func-vector 0) 2)
=> 5
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
964 次 |
| 最近记录: |