当我将一些元数据附加到一个函数然后调用它时,我无法访问该函数中的那些元数据
(let [I (fn I [x] (println I) (println (meta I)))]
(let [f (with-meta I {:rr 5})]
(println I)
(println f)
(f I)))
我看到来自函数内部的自引用不是实际调用的函数实例,因此通过该自引用没有可用的元数据。我需要自我引用来给我实际调用的函数实例,以访问这些元数据
答案 0 :(得分:0)
我认为问题在于您将函数的值和函数的标识混合在一起。这是许多其他语言所做的事情,因此在学习Clojure时很自然。在您的示例中,I
有一个自身引用,并从该引用中查找元数据,该元数据返回nil
。然后,您创建与f
相同的I
,但具有一些元数据。因此,当您运行f
时,它将在I
上查找元数据并返回nil
。定义f
根本不会改变I
,它只是根据旧事物创建了新事物。如果要更改某些内容,则需要引入可以更改的引用类型。其中有几种,但是通常要使用Var
(see here for reference)
(defn i [] (meta i))
(i) ;;=> nil
(alter-var-root #'i with-meta {:rr 5})
(i) ;;=> {:rr 5}
在这里,我们在当前名称空间中定义一个名为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)))
但是,除了学习如何将这些东西组合在一起之外,我不确定目标是什么。在计划维护的真实程序中尝试使用这些结构可能是一个坏主意。
答案 1 :(得分:0)
我偶然发现了一个使函数能够读取其自身元数据的技巧。看起来,当原始函数定义具有自定义元数据时,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
答案 2 :(得分:0)
欢迎来到Clojure,@ xstreamer!
我将提出与您要求的(准确地)有所不同的建议。我不知道如何从函数 中查询函数的元数据。因此,我建议您先定义函数,然后再重新定义函数元数据。在Clojure中,这相当简单。
(defn f
"Boring doc"
[])
(meta #'f)
;; => {:arglists ([]),
;; :doc "Boring doc",
;; :line 32,
;; :column 1,
;; :file "C:/Users/teodorlu/IdeaProjects/th-scratch/src/th/play/core.clj",
;; :name f,
;; :ns #object[clojure.lang.Namespace 0x3b402f0c "th.play.core"]}
现在,重新定义它!
(alter-meta! #'f assoc :rr 5)
(meta #'f)
;; => {:arglists ([]),
;; :doc "Boring doc",
;; :line 32,
;; :column 1,
;; :file "C:/Users/teodorlu/IdeaProjects/th-scratch/src/th/play/core.clj",
;; :name f,
;; :ns #object[clojure.lang.Namespace 0x3b402f0c "th.play.core"],
;; :rr 5}
assoc
在地图上设置值的地方。
(assoc {} :rr 5)
;; {:rr 5}
(assoc {:some :stuff} :more :stuff)
;; {:some :stuff, :more :stuff}
如果您对#'f
感到困惑,这就是获取表示f 绑定的 var的方法,而不仅仅是它所引用的值。有关var及其使用方法的详细信息,请参考official reference on vars和less terse guide from 8th light。