在Clojure中执行动态绑定函数

Car*_*icz 5 binding eval clojure

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

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

这是我的小型自包含示例:

(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
)))
Run Code Online (Sandbox Code Playgroud)

如果您想知道我的测试设置,要查看这6个语句的结果,我评论/取消注释OK /失败行的具体内容,然后(runit)从REPL 调用.

是否有一个简单的解决方法,我可以保证让eval"d quote到功能d调用另一个函数内部定义的函数工作?


更新:

这(基于danlei的建议)确实有效.让我们看看我是否可以在"现实生活中"使用这种方法!

(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!
))
Run Code Online (Sandbox Code Playgroud)

更新:

这段代码进入我的约束满足问题的解决方案- 我想知道谁拥有斑马!我对Clojure很熟悉,尤其是函数式编程,这使得练习非常具有挑战性.我陷入了很多陷阱,但我很乐意,因为这是学习经历的一部分.

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

[:con-eq :spain :dog]
[:abs-pos :norway 1]
[:con-eq :kools :yellow]
[:next-to :chesterfields :fox]
Run Code Online (Sandbox Code Playgroud)

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

'(coloc :japan :parliament) ; 10
'(coloc :coffee :green) ; 12
'(next-to :chesterfield :fox) ; 5
Run Code Online (Sandbox Code Playgroud)

所以我可以用简单的方式发送约束规则eval.这似乎更优雅和"lisp-y".但是,这些函数中的每一个都需要访问我的域数据(命名vars),并且这些数据会随着程序的运行而不断变化.我不想通过引入额外的参数来玷污我的规则,所以我希望通过动态范围vars可用于eval'd函数.

我现在已经知道动态范围可以使用binding,但它也需要一个declare.

dan*_*lei 5

你的意思是这样的吗?

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

(binding [bar (fn [] (println "hello bar"))]
  (eval foo))
Run Code Online (Sandbox Code Playgroud)

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

(let [foo 1]
  (eval 'foo))
Run Code Online (Sandbox Code Playgroud)

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

(declare foo)

(binding [foo 1]
  (eval 'foo))
Run Code Online (Sandbox Code Playgroud)

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

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