2024 Clojure状态调查!中分享你的想法。

欢迎!请参阅关于页面以获取有关如何使用本站的一些更多信息。

0
Clojure

在Clojure中,符号可以选择属于一个命名空间。

:FileType/TXT

能否列出为给定的关键字命名空间定义的所有关键字?

2 答案

0

编辑
 
最佳答案

按 Andy 的答案和评论实现的此功能

(defn kws-with-ns [ns]
  (try
    (let [tf (.getDeclaredField clojure.lang.Keyword "table")]
      (.setAccessible tf true)
      (let [table (.get tf nil)]
        (->> (.keySet table)
             (filter #(= ns (namespace %)))
             (map #(.get table %))
             (map #(.get %)))))
    (catch Throwable _
      ())))
我没有找到根本原因,但你的代码对我来说不起作用,我以前用过这种方法,但从未改变过 JVM 类的修饰符标志,所以提供一个对我来说起作用的替代函数。 与你的代码不同,它可以使异常对调用者可见,并返回所有关键字而不是仅返回特定命名空间中的关键字。 这些更改并不是实质性的,只是有所不同,但我怀疑 getModifiers 调用和 setInt 完全是不必要的。

(defn all-kws []
  (let [tf (.getDeclaredField clojure.lang.Keyword "table")]
    (.setAccessible tf true)
    (let [table (.get tf nil)]
      (->> (.keySet table)
           (map #(.get (.get table %)))))))

(def x (->> (all-kws)
            (filter #(= (namespace %) "user"))))
是的,这并不必要,'setAccessible(true)' 就足够了。代码片段已经更新。
+1

顺便说一句,虽然通常所说的“命名空间”部分不需要是一个真正的命名空间,因为官方 Clojure 文档(例如在这个页面 https://clojure.org/reference/reader )称它们为限定符关键词,并且在关键词名称中 '/' 前面的部分是限定符。

其中一个原因是,虽然限定符可以与程序中的命名空间相同,但它根本就不需要是一个命名空间。

在 Clojure/Java 中,所有关键词都属于 'interned',即在运行时读取或动态创建时,意味着所有具有相同名称的关键词都是内存中相同的唯一 Java 对象。为了实现这一点,所有关键词都存储在公共表中。您可以在 Clojure 实现中名为 Keyword.java 的 Java 文件中看到这一点,名为 table。它被声明为 private,并且没有公共方法可以访问它,除了通过 intern 创建新的关键词和 find 来查看是否已创建具有特定名称的关键词。

使用 Java 反射 API 穿越这个 private 限制相当简单,除非您的 JVM 是在阻止此操作的安全选项启动的。可以使用这些方法访问私有 table 字段的 内容,遍历其所有条目,并返回它们所有,然后可以使用 Clojure 或 Java 代码过滤它们,只保留您感兴趣的内容。

我对 ClojureScript 关键词的实现了解不多,无法说在这里可以实现什么。

趣闻:ClojureScript 中的等效关键词不一定必须是 `identical?`!存在一个特殊的谓词 `keyword-identical?`。
...