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 vote
by

顺便说一下,这样的关键字的一部分通常被称为“命名空间”,但并不一定是现有的命名空间。官方Clojure文档(例如这个页面 https://clojure.org/reference/reader )称它们为限定符关键字,关键字名中'/'之前的部分是限定符。

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

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

使用Java反射API绕过private限制相当简单,除非你的JVM是在防止这种做法的安全选项下启动的。可以通过这些方法访问私有table字段的全部内容,迭代其所有条目,然后返回它们,然后你可以使用Clojure或Java代码过滤它们,并只保留你感兴趣的关键字。

我对ClojureScript中关键字实现的了解不足以说明在那里可能实现什么。

by
趣闻:在ClojureScript中,等效的关键字不一定是`identical?`!还有专门用处的谓词`keyword-identical?`。
...