【问题标题】:Executing a dynamically bound function in Clojure在 Clojure 中执行动态绑定函数
【发布时间】:2011-02-04 05:44:04
【问题描述】:

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

这对于使用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
)))

如果您想知道我的测试设置,请查看这 6 条语句的结果,我注释/取消注释特定于 OK/failing 行,然后从 REPL 调用 (runit)

我可以采取一个简单的修复方法来让eval'd quoted 调用函数来为另一个函数中定义的函数工作吗?


更新:

这(基于 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!
))

更新:

此代码将进入我的Constraint Satisfaction Problem 解决方案 - 我想找出who owns the zebra!我对 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 发送约束规则。这似乎更加优雅和“lisp-y”。但是,这些函数中的每一个都需要访问我的域数据(名为vars),并且这些数据会随着程序的运行而不断变化。我不想通过引入额外的参数来破坏我的规则,所以我希望 vars 通过动态作用域对 eval'd 函数可用。

我现在了解到可以使用 binding 完成动态范围,但它还需要 declare

【问题讨论】:

  • 您是在努力解决问题还是真的在尝试实现某些东西?如果是后者,我想知道你试图做什么,迫使你使用这样的设计——乍一看延迟或普通的闭包可以解决问题。
  • @cgrand:感谢您的关注!我添加了第二个更新来解释我想要做什么。这同时对我有用,但我当然愿意接受更好的建议!
  • @cgrand:我的帖子Constraint Satisfaction Problem 现在有了我的完整解决方案。如果你有兴趣,你可以在那里看到binding。对于我的业余方法,我非常愿意接受建设性的批评。

标签: binding clojure eval


【解决方案1】:

你的意思是这样的吗?

(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:

在当前动态环境和空词法环境中评估表单。

【讨论】:

  • 是的,似乎有效。很高兴有越来越多的 SO 上熟悉 Clojure 的人。谢谢!
  • 不客气。我想这是出于教育目的。否则,请考虑 cgrand 对您的问题的评论。
【解决方案2】:

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

【讨论】:

  • 谢谢!我很高兴确认这正是我正在做的事情。我的同时工作解决方案的编码可以在我的Constraint Satisfaction Problem 帖子中找到。
猜你喜欢
  • 2012-09-29
  • 1970-01-01
  • 2013-05-31
  • 1970-01-01
  • 1970-01-01
  • 2011-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多