【问题标题】:Creating mutually recursive local functions with metadata in Clojure在 Clojure 中使用元数据创建相互递归的本地函数
【发布时间】:2019-02-08 06:57:54
【问题描述】:

假设我想在本地范围内定义两个相互递归的函数。我可以用 letfn 做到这一点:

(letfn 
  [(f [x] (if (= x 0) (g x) true))
   (g [x] (if (= x 1) (f x) false))]
 (f 0))

但与let 相比,letfn 受到很大限制,因为它只接受“函数规范”,而不接受任意表达式。我的问题是:如果我想将元数据(使用with-meta)附加到fg,以便在f 内,我可以读取g 的元数据,在g 内,我可以读取f 的元数据吗?这在 Clojure 中可行吗?

(对于上下文,我正在尝试实现一个类似fn 的宏,该宏会自动将某些元数据附加到正在创建的函数中。我希望这些自动注释的fns 可以在普通Clojure 函数的任何地方进行实例化是,包括在letfn 中。但我不知道如何定义一个类似letfn 的宏来附加元数据,因为它最终必须对letfn 进行脱糖,它不能附加元数据。)

【问题讨论】:

  • 这一切的目的是什么?将元数据附加到函数很少有用,我想它在单个词法范围内的用处会更小,因为所有附近的函数无论如何都“知道”彼此。
  • @amalloy 用例是在 Clojure 中嵌入一种新语言。该语言的“入口点”是特殊的宏,其语法与fn/letfn/defn 相同,但将附加元数据附加到定义的函数对象。该语言中的函数可以像常规 Clojure 函数一样执行,但也可以以其他方式执行——对于那些,元数据是必要的。

标签: clojure


【解决方案1】:

别忘了with-local-vars

  (with-local-vars [f (fn [x] (if (= x 0) (g x) true))
                    g (fn [x] (if (= x 1) (f x) false))]
    (reset-meta! f {:f 3})
    (reset-meta! g {:g 2})

结果:

(f 0) => false
(f 1) => true

f           => #<Var: --unnamed-->
(var-get f) => #object[tst.demo.core$fn__20698$fn__20699 0x1eb2d718 "tst.demo.core$fn__20698$fn__20699@1eb2d718"]
(meta f)    => {:f 3}
(meta g)    => {:g 2}

您还可以使用var-getvar-set 访问/更改本地变量的值。

【讨论】:

  • 整洁。我没有想到with-local-vars 的这个用例。这似乎有点违背预期的用例,但它确实完成了工作。
【解决方案2】:

我找到了以下解决方案,其中letfn 用于定义计算结果为实际函数fg 的thunk:

(letfn [(f-thunk [] 
          (with-meta (fn f [] (let [g (g-thunk)] (meta g))) {:f 3})) 
        (g-thunk [] 
          (with-meta (fn g [] (let [f (f-thunk)] (meta f))) {:g 2}))]
  (let [f (f-thunk) g (g-thunk)]
    [(f) (g)]))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-13
    • 2016-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多