在Clojure中执行动态绑定函数

时间:2010-04-04 17:31:09

标签: binding clojure eval

我想在数据结构中预先存储一堆函数调用,然后在另一个函数中评估/执行它们。

这对于使用defn在命名空间级别定义的函数按计划工作(即使函数定义在我创建数据结构之后),但不适用于let [name (fn或{{定义的函数1}}在函数内部。

这是我自己的小例子:

letfn

如果您对我的测试设置感到疑惑,要查看这6个语句的结果,我会评论/取消注释OK /失败行的具体内容,然后从REPL中调用(def todoA '(funcA)) (def todoB '(funcB)) (def todoC '(funcC)) (def todoD '(funcD)) ; unused (defn funcA [] (println "hello funcA!")) (declare funcB funcC) (defn runit [] (let [funcB (fn [] (println "hello funcB"))] (letfn [(funcC [] (println "hello funcC!"))] (funcA) ; OK (eval todoA) ; OK (funcB) ; OK (eval todoB) ; "Unable to resolve symbol: funcB in this context" at line 2 (funcC) ; OK (eval todoC) ; "Unable to resolve symbol: funcC in this context" at line 3 )))

是否有一个简单的修复方法可以让(runit)'d eval d调用函数来为另一个函数中定义的函数工作?


更新

这(基于danlei的建议) 工作。让我们看看我是否可以在“现实生活中”使用这种方法。

quote

更新

此代码将进入我的Constraint Satisfaction Problem解决方案 - 我想找出who owns the zebra!我对Clojure很熟悉,尤其是函数式编程,这使得练习非常具有挑战性。我陷入了很多陷阱,但我很乐意,因为这是学习经历的一部分。

我曾经将约束指定为一堆简单的向量,如下所示:

(def todoB '(funcB))
(declare funcB)

(defn runit []
  (binding [funcB (fn [] (println "hello funcB"))]
    (funcB)
    (eval todoB)  ; "Unable to resolve symbol: funcB in this context" at line 1!
))

其中每个向量的第一个将指定约束的类型。但这导致我对这些规则的调度机制的笨拙实现,所以我决定将它们编码为(引用)函数调用:

[:con-eq :spain :dog]
[:abs-pos :norway 1]
[:con-eq :kools :yellow]
[:next-to :chesterfields :fox]

所以我可以用简单的'(coloc :japan :parliament) ; 10 '(coloc :coffee :green) ; 12 '(next-to :chesterfield :fox) ; 5 发送约束规则。这似乎更优雅和“lisp-y”。但是,这些函数中的每一个都需要访问我的域数据(名为eval),并且这些数据会随着程序的运行而不断变化。我不想通过引入额外的参数来玷污我的规则,因此我希望vars通过动态范围可用于vars'd函数。

我现在已经了解到动态范围可以使用eval完成,但它也需要binding

2 个答案:

答案 0 :(得分:5)

你的意思是这样吗?

(def foo '(bar))
(declare bar)

(binding [bar (fn [] (println "hello bar"))]
  (eval foo))

如果是,您的问题将减少到:

(let [foo 1]
  (eval 'foo))

这不起作用,因为eval不会在词汇环境中进行评估。你可以使用vars解决这个问题:

(declare foo)

(binding [foo 1]
  (eval 'foo))

就这一点而言,Clojure似乎与CL具有相似的语义,参见CLHS

  

评估当前动态环境和null词法环境中的表单。

答案 1 :(得分:3)

我认为你正在解决错误的问题。在函数式语言中,函数是值,并且可以分配给可以存储任何其他值的任何值,例如,一张地图。你不应该试图操纵命名空间或评估任何东西 - 这不是perl。

尝试这样的操作,并使用assoc在本地更改地图:

user=> (def fnmap {:funcA (fn [x] (inc x)), :funcB (fn [x] (* x 2))})
#'user/fnmap
user=> ((:funcA fnmap) 10)
11
user=> ((:funcB fnmap) 10)
20