请分享您的想法,参加 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?`。
...