Clojure函数自引用以获取自己的元数据
当我将一些元数据附加到函数并调用它时,我无法访问该函数中的那些元数据Clojure函数自引用以获取自己的元数据,clojure,Clojure,当我将一些元数据附加到函数并调用它时,我无法访问该函数中的那些元数据 (let [I (fn I [x] (println I) (println (meta I)))] (let [f (with-meta I {:rr 5})] (println I) (println f) (f I))) 我看到函数中的自引用不是实际调用的函数实例,因此没有元数据可通过该自引用使用。我需要自引用来提供实际调用的函数实例,以访问这些元数据。我认为问题在于您将函数的值和函数的标识
(let [I (fn I [x] (println I) (println (meta I)))]
(let [f (with-meta I {:rr 5})]
(println I)
(println f)
(f I)))
我看到函数中的自引用不是实际调用的函数实例,因此没有元数据可通过该自引用使用。我需要自引用来提供实际调用的函数实例,以访问这些元数据。我认为问题在于您将函数的值和函数的标识混为一谈。这是许多其他语言都会做的事情,所以当你学习Clojure时,这是很自然的。在您的示例中,
I
有一个对自身的引用,并从该引用中查找元数据,该引用返回nil
。然后创建与I
相同的f
,但带有一些元数据。因此,当您运行f
时,它会在I
上查找元数据并返回nil
。定义f
根本不会改变I
,它只是根据旧事物创建了一个新事物。如果要更改某些内容,则需要引入一个可以更改的引用类型。其中有几种,但通常要使用Var
在这里,我们在当前名称空间中定义了一个名为i
的函数,它只返回自己的元数据。我们称之为获得nil
。然后我们用一些新的元数据修改全局引用,并再次调用它
如果您想要一个更具词汇范围的示例,可以使用atom
,如下所示:
(let [i (atom nil)
f (fn [] (meta @i))]
(reset! i f)
(prn 'before '>> (@i))
(swap! i with-meta {:rr 5})
(prn 'after '>> (@i)))
然而,除了学习这些东西如何结合在一起,我不确定目标是什么。在您计划维护的实际程序中尝试使用这些结构可能不是一个好主意。我认为问题在于您将函数的值和函数的标识混为一谈。这是许多其他语言都会做的事情,所以当你学习Clojure时,这是很自然的。在您的示例中,
I
有一个对自身的引用,并从该引用中查找元数据,该引用返回nil
。然后创建与I
相同的f
,但带有一些元数据。因此,当您运行f
时,它会在I
上查找元数据并返回nil
。定义f
根本不会改变I
,它只是根据旧事物创建了一个新事物。如果要更改某些内容,则需要引入一个可以更改的引用类型。其中有几种,但通常要使用Var
在这里,我们在当前名称空间中定义了一个名为i
的函数,它只返回自己的元数据。我们称之为获得nil
。然后我们用一些新的元数据修改全局引用,并再次调用它
如果您想要一个更具词汇范围的示例,可以使用atom
,如下所示:
(let [i (atom nil)
f (fn [] (meta @i))]
(reset! i f)
(prn 'before '>> (@i))
(swap! i with-meta {:rr 5})
(prn 'after '>> (@i)))
然而,除了学习这些东西如何结合在一起,我不确定目标是什么。在您计划维护的实际程序中尝试使用这些结构可能不是一个好主意。我偶然发现了一个让函数能够读取自己的元数据的技巧。当原始函数定义具有自定义元数据时,Clojure编译器生成元数据支持代码的方式似乎有所不同。如果存在,
(meta-fn-name)
在函数体内部工作,否则不工作。例如,以下操作生成OP所需的结果:
*clojure-version*
;;=> {:major 1, :minor 10, :incremental 0, :qualifier nil}
(let [f1 ^{:foo true} (fn f [] (meta f))
f2 (with-meta f1 {:bar true})]
(prn (f1))
(prn (f2)))
;;=> {:foo true}
;;=> {:bar true}
;;=> nil
我们可以检查在原始定义中没有元数据的情况下为函数生成的代码-只有invoke
方法
(require '[clojure.pprint :as p])
(let [ff (fn f [] (meta f))]
(p/pprint (seq (.getDeclaredMethods (class ff)))))
;;=> (#object[java.lang.reflect.Method 0x2b56b137 "public java.lang.Object user$eval2171$f__2172.invoke()"])
;;=> nil
当元数据存在时,会生成其他方法(meta
和withMeta
)来处理元数据
(let [ff ^:foo (fn f [] (meta f))]
(p/pprint (seq (.getDeclaredMethods (class ff)))))
;;=> (#object[java.lang.reflect.Method 0x3983bd83 "public clojure.lang.IObj user$eval2175$f__2176.withMeta(clojure.lang.IPersistentMap)"]
;;=> #object[java.lang.reflect.Method 0x547d182d "public clojure.lang.IPersistentMap user$eval2175$f__2176.meta()"]
;;=> #object[java.lang.reflect.Method 0x62c3d0fe "public java.lang.Object user$eval2175$f__2176.invoke()"])
;;=> nil
我无意中发现了一个技巧,使函数能够读取自己的元数据。当原始函数定义具有自定义元数据时,Clojure编译器生成元数据支持代码的方式似乎有所不同。如果存在,
(meta-fn-name)
在函数体内部工作,否则不工作。例如,以下操作生成OP所需的结果:
*clojure-version*
;;=> {:major 1, :minor 10, :incremental 0, :qualifier nil}
(let [f1 ^{:foo true} (fn f [] (meta f))
f2 (with-meta f1 {:bar true})]
(prn (f1))
(prn (f2)))
;;=> {:foo true}
;;=> {:bar true}
;;=> nil
我们可以检查在原始定义中没有元数据的情况下为函数生成的代码-只有invoke
方法
(require '[clojure.pprint :as p])
(let [ff (fn f [] (meta f))]
(p/pprint (seq (.getDeclaredMethods (class ff)))))
;;=> (#object[java.lang.reflect.Method 0x2b56b137 "public java.lang.Object user$eval2171$f__2172.invoke()"])
;;=> nil
当元数据存在时,会生成其他方法(meta
和withMeta
)来处理元数据
(let [ff ^:foo (fn f [] (meta f))]
(p/pprint (seq (.getDeclaredMethods (class ff)))))
;;=> (#object[java.lang.reflect.Method 0x3983bd83 "public clojure.lang.IObj user$eval2175$f__2176.withMeta(clojure.lang.IPersistentMap)"]
;;=> #object[java.lang.reflect.Method 0x547d182d "public clojure.lang.IPersistentMap user$eval2175$f__2176.meta()"]
;;=> #object[java.lang.reflect.Method 0x62c3d0fe "public java.lang.Object user$eval2175$f__2176.invoke()"])
;;=> nil
欢迎来到Clojure,@xstreamer 我将提出一些与你(确切地)要求的不同的建议。我真的不知道如何从函数内部查询函数的元数据。所以我建议先定义函数,然后再重新定义函数元数据。这在Clojure中相当简单
(defn f
“无聊的文件”
[])
(meta#f)
;; => {:arglists([]),
:博士“无聊博士”,
第32行,
第1栏,
;;:文件“C:/Users/teodorlu/IdeaProjects/th scratch/src/th/play/core.clj”,
名字f,
;:ns#对象[clojure.lang.Namespace 0x3b402f0c“th.play.core”]}
现在,重新定义它
(alter meta!#'f assoc:rr 5)
(meta#f)
;; => {:arglists([]),
:博士“无聊博士”,
第32行,
第1栏,
;;:文件“C:/Users/teodorlu/IdeaProjects/th scratch/src/th/play/core.clj”,
名字f,
;:ns#对象[clojure.lang.Namespace 0x3b402f0c“th.play.core”],
;;:rr 5}
其中,assoc
在映射中设置一个值
(assoc{}:rr5)
;; {:rr 5}
(assoc{:some:stuff}:more:stuff)
;; {:some:stuff,:more:stuff}
工具书类
如果您对#'f
感到困惑,那么这就是获得表示f绑定的var的方式,而不仅仅是它引用的值。更多信息