代码之家  ›  专栏  ›  技术社区  ›  Carl Smotricz

在Clojure中执行动态绑定函数

  •  5
  • Carl Smotricz  · 技术社区  · 14 年前

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

    这与在命名空间级别为 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条语句的结果,我对确定/失败行进行了特定的注释/取消注释,然后调用 (runit) 来自RePL。

    我能保证得到一个简单的解决办法吗 eval D quote d调用函数来为另一个函数内定义的函数工作?


    更新:

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

    (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
    

    所以我可以用一个简单的 埃瓦 . 这看起来更优雅,“lisp-y”。但是,这些函数中的每一个都需要访问我的域数据(命名为 vars ,并且随着程序的运行,这些数据会不断变化。我不想通过引入一个额外的论点来破坏我的规则,所以我想 瓦尔斯 提供给 埃瓦 'D通过动态范围界定功能。

    我现在了解到,可以使用 binding 但它也需要 declare .

    2 回复  |  直到 14 年前
        1
  •  5
  •   Michał Marczyk    14 年前

    你的意思是这样的吗?

    (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,cf.相似。 CLHS 以下内容:

    在当前动态环境和空词汇环境中计算窗体。

        2
  •  3
  •   Chris    14 年前

    我认为你是在解决错误的问题。在函数语言中,函数是值,可以分配给任何可以存储任何其他值的对象,例如映射。您不应该试图操纵名称空间或规避任何东西——这不是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