在 Clojure 中执行动态绑定函数
我想在数据结构中预先存储一堆函数调用,然后从另一个函数中评估/执行它们。
对于使用 defn
在命名空间级别定义的函数,这按计划工作(即使函数定义是在我创建数据结构之后进行的),但不适用于 let [name (fn
或 letfn
在函数中,
这是我的小型独立示例:
(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
)))
如果您对我的测试设置感到好奇,请查看我注释/取消注释的这 6 条语句的结果。好的/失败的行,然后从 REPL 调用 (runit)
是否有一个简单的修复方法可以让我获得 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!
))
更新:
此代码将进入我的约束满足问题<的解决方案/a> - 我想找出谁拥有斑马!我对 Clojure 尤其是函数式编程相当陌生,这使得这个练习非常具有挑战性。我掉进了很多坑,但我对此很满意,因为这是学习经历的一部分。
我曾经将约束指定为一堆简单的向量,如下所示:
[: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
这样我就可以使用简单的 eval
来调度约束规则。这看起来更加优雅和“口齿不清”。然而,这些函数中的每一个都需要访问我的域数据(名为vars
),并且这些数据随着程序运行而不断变化。我不想通过引入额外的参数来破坏我的规则,因此我希望通过动态作用域可将 vars
用于 eval
的函数。
我现在了解到可以使用绑定
来完成动态作用域,但它还需要声明
。
I'd like to pre-store a bunch of function calls in a data structure and later evaluate/execute them from within another function.
This works as planned for functions defined at namespace level with defn
(even though the function definition comes after my creation of the data structure) but will not work with functions defined by let [name (fn
or letfn
inside the function.
Here's my small self-contained example:
(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
)))
In case you're wondering about my test setup, to see the result of those 6 statements I comment/uncomment specific of the OK/failing lines and then call (runit)
from the REPL.
Is there a simple fix I could undertake to get eval
'd quote
d calls to functions to work for functions defined inside another function?
Update:
This (based on danlei's suggestion) does work. Let's see if I can get this method working in "real life!"
(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!
))
Update:
This code is going into my solution for a Constraint Satisfaction Problem - I want to find out who owns the zebra! I'm fairly new to Clojure and especially functional programming, and this has made the exercise quite challenging. I'm falling into a lot of pits but I'm OK with that as it's part of the learning experience.
I used to specify the constraints as a bunch of simple vectors, like this:
[:con-eq :spain :dog]
[:abs-pos :norway 1]
[:con-eq :kools :yellow]
[:next-to :chesterfields :fox]
where the first of each vector would specify the kind of constraint. But that led me to an awkward implementation of a dispatch mechanism for those rules, so I decided to encode them as (quoted) function calls instead:
'(coloc :japan :parliament) ; 10
'(coloc :coffee :green) ; 12
'(next-to :chesterfield :fox) ; 5
so I can dispatch the constraining rule with a simple eval
. This seems a lot more elegant and "lisp-y." However, each of these functions needs to access my domain data (named vars
), and this data keeps changing as the program runs. I didn't want to blemish my rules by introducing an extra argument, so I wanted vars
to be available to the eval
'd functions via dynamic scoping.
I've now learned that dynamic scoping can be done using binding
, but it also needs a declare
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你的意思是这样的吗?
如果是,您的问题可以简化为:
这不起作用,因为 eval 不在词法环境中求值。您可以使用 vars 来解决这个问题:
就这一点而言,Clojure 似乎与 CL 具有相似的语义,参见。 CLHS:
Do you mean something like this?
If yes, your problem reduces to this:
This won't work, because eval does not evaluate in the lexical environment. You can get around that using vars:
As far as that is concerned, Clojure seems to have similar semantics to CL, cf. the CLHS:
我认为你正在解决错误的问题。在函数式语言中,函数是值,可以分配给任何可以存储任何其他值的东西,例如映射。您不应该尝试操作名称空间或评估任何内容 - 这不是 Perl。
尝试这样的操作,并使用 assoc 在本地更改地图:
I think you're solving the wrong problem. In functional languages, functions are values and can be assigned to anything that can store any other value, e.g. a map. You shouldn't be trying to manipulate namespaces or evaling anything - this isn't perl.
Try something like this, and use assoc to change the map locally: