请在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 中关键词的实现知之甚少,无法确定那里可以做些什么。

by
有趣的事实:在 ClojureScript 中,等效的关键词不一定总是 `identical?`!有一个特定目的的谓词 `keyword-identical?`。
...