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"))))
by
这是不必要的,'setAccessible(true)' 就足够了。已更新片段。
+1 投票
by

此外,请注意,这样的关键字中通常被称为 "命名空间" 的部分根本不需要是现有的命名空间。 正式的 Clojure 文档(例如在此页面上 https://clojure.org/reference/reader ) 称之为限定关键字,而在关键字名称中 '/' 前的部分是限定符。

这其中的一个原因是因为限定符可以是你的程序中的命名空间,但它根本不需要是一个命名空间。

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

使用 Java 反射 API 绕过 private 限制相当直接,除非你的 JVM 是带有防止这种操作的安全选项启动的。 可以使用这些方法来访问该私有 table 字段的内容,遍历其所有条目,然后返回它们全部,然后您可以使用 Clojure 或 Java 代码过滤它们,仅保留您感兴趣的内容。

我对 ClojureScript 关键字的实现了解不够多,无法说清在那里什么是可能的。

趣闻:ClojureScript 中的等价关键词不一定必定是 `identical?`!有一个专门的谓词函数 `keyword-identical?`。
...