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的方式,而不仅仅是它引用的值。更多信息