2011-12-01 26 views
4

我今天开始使用Clojure,偶然发现可以在运行时动态更改函数的说法。 这听起来很酷,所以我使用这个功能编写了一小段代码。使用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

回答

4

使用动态绑定的大多是品味的问题,但也有一些注意事项:

动态绑定是非常的明确传递调用堆栈上值的快捷方式。只有少数情况下,这是一个非常明显的胜利;主要是像通过“不支持它们的API”传递“全局”配置设置/参数“。

依赖于动态绑定的API很难包装成更明确的东西,而其他方式则更容易(通常可以半自动地完成)。

动态绑定对惰性序列或其他任何在当前调用堆栈之外进行评估的其他任何东西(如其他线程)都不会很好。总而言之,我认为“更干净”的功能解决方案是将state作为参数传递给parity,但是可以采取任何方式。

+0

感谢您的见解,Joost!所以我理解你是对的,我做的不是亵渎吗? 我知道这个事实,可以用不同的方式编写这个功能。但是,我是否至少在Clojure中捕获了动态绑定的想法? – Zakum

+0

个人而言,我会尝试在使用动态绑定之前使用显式值传递,这主要是因为我提到的绑定的缺点,但它可以使代码更加清晰和容易。 –

2

虽然能够动态地将一个符号绑定到不同的函数上,但我猜你正在重新定义一个函数。

认为它是这样的:你的代码创建一个符号,两个函数,你动态绑定的符号,以不同的功能:

        +---> func1 
           /
symbol ---- [dynamic binding] ---< 
            \ 
            +---> func2 

动态绑定的作用仅限于的范围binding调用。

我们要实现的是什么,给定一个符号和功能,提供了功能的新的实现让所有人都认为是指它的代码将访问新的实现:

(defn func1 [...]) 

(var func1) ; ---> func1 

(defn func1 [...]) 

(var func1) ; ---> func1* 

和这样的更改将永久影响所有使用func1的代码。当你开发一个clojure时,这是一个正常的任务:你很可能会在正在运行的应用程序中打开一个REPL,并且你会多次重复定义相同的符号defdefn,重新定义所有的运动部件您的应用程序在飞行中。

如果您使用的是Emacs和SLIME/Swank,只要您在修改的Clojure源文件上点击C-c C-k,就可能重新定义名称空间中的所有函数,而无需重新启动应用程序。

+0

我真的不认为这是我的(我不知道,但^^)。 你告诉我的是我可以写一个函数,使用它,重新定义它,再次使用它,等等,对吧?对我来说,这是静态的,因为更改是由程序员手动完成的。 我有兴趣编写在运行时**改变其他功能**的函数。更实际的例子是将日志记录添加到现有功能。 一个改变自己的函数将是一个更奇特的例子。 – Zakum

+0

Clojure中的AFAIK动态变量在Var绑定上运行,而不是在符号上运行。 – siefca