假设我想在本地范围内定义两个相互递归的函数。我可以用letfn做到这一点:
(letfn
[(f [x] (if (= x 0) (g x) true))
(g [x] (if (= x 1) (f x) false))]
(f 0))
但是与let
相比,letfn
是相当有限的,因为它只接受"函数规范",而不是任意表达式。我的问题是:如果我想将元数据(使用 with-meta
(附加到f
和g
,以便在f
内,我可以读取g
的元数据,而在g
中,我可以读取f
的元数据怎么办?这在克洛朱尔可能吗?
(对于上下文,我正在尝试实现一个类似fn
的宏,该宏会自动将某些元数据附加到正在创建的函数。我希望这些自动注释的fn
可以在普通 Clojure 函数所在的任何地方实例化,包括在letfn
内。但是我不明白如何定义一个类似letfn
的宏来附加元数据,因为它最终必须脱糖到letfn
,它不能附加元数据。
不要忘记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-get
和 var-set
来访问/更改本地变量的值。
我找到了以下解决方案,其中letfn
用于定义计算实际函数f
和g
的thunks:
(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)]))