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 中,所有关键字在读或创建时都会被'内部化',这意味着所有具有同一名称的关键字在内存中都是相同的Java对象。为了实现这一点,所有关键字都存储在公共表中。你可以在 Clojure 实现的 Java 文件 Keyword.java 中看到这一点,命名为 table。它被声明为私有,并且除了通过intern创建新关键字和通过find查看是否存在特定名称的关键字之外,没有公共方法可以访问它。

使用Java反射API绕过private限制相对简单,除非你的JVM是用防止这种操作的选项启动的。可以利用这些方法来访问私有table字段的值,遍历其所有条目,并将它们全部返回,然后你可以使用Clojure或Java代码来过滤,只保留你感兴趣的部分。

我对ClojureScript中关键字实现的了解不足以说明那边的可能性。

 pbwolf的头像 by
趣闻:在ClojureScript中,等价的关键字不一定是`identical?`!存在一个专门用于比较的谓词`keyword-identical?`。
...