我今天开始玩Clojure,偶然发现了一个可以在运行时动态更改函数的声明。 这听起来很酷,所以我用这个功能写了一小段代码。
(defn ^:dynamic state [x]
(odd x))
(defn even [x]
(if (= x 0)
(println "even")
(binding [state odd] (parity x))))
(defn odd [x]
(if (= x 0)
(println "odd")
(binding [state even](parity x))))
(defn parity [x]
(state (dec x)))
一切都很好,但由于我完全不熟悉Clojure,我不知道这是不是
a)干净的功能代码(因为奇数甚至似乎有副作用?)
b)改变运行时功能的方式应该完成
我很感激你的任何建议! :) -Zakum
答案 0 :(得分:4)
使用动态绑定主要是品味问题,但有一些注意事项:
动态绑定几乎是在调用堆栈上显式传递值的快捷方式。只有少数情况下这样做是完全明显的胜利;大多数事情,如传递"全球"配置设置/参数"通过"不支持它们的API。
依赖于动态绑定的API很难包含在更明确的内容中,而另一种方式则更容易(通常可以半自动完成)。
动态绑定不适用于延迟序列或在当前调用堆栈之外计算的任何其他内容(与其他线程一样)。
总而言之,我认为"清洁"功能解决方案是将state
作为参数传递给parity
,但可以以任何方式进行参数。
答案 1 :(得分:2)
虽然能够动态地将符号绑定到不同的函数,但我猜你所追求的是真正重新定义函数。
以这种方式思考:您的代码创建一个符号和两个函数,并将符号动态绑定到另一个函数:
+---> func1
/
symbol ---- [dynamic binding] ---<
\
+---> func2
动态绑定的效果仅限于binding
调用的范围。
我们想要实现的是,给定符号和函数,为函数提供一个新的实现,以便引用它的所有代码都将访问新的实现:
(defn func1 [...])
(var func1) ; ---> func1
(defn func1 [...])
(var func1) ; ---> func1*
此类更改会永久影响使用func1
的所有代码。当你开发一个clojure时,这是一个正常的任务:你很可能在一个正在运行的应用程序上打开了一个REPL,你将def
和defn
几次相同的符号和重新定义,动态重新定义应用程序的所有移动部分。
如果您正在使用Emacs和SLIME / Swank,那么每次在修改过的Clojure源文件上点击C-c C-k
时,您都可能重新定义命名空间中的所有函数,而无需重新启动应用程序。 / p>