2024年 Clojure 状态调查!分享你的想法。

欢迎!请参阅关于页面以了解更多信息。

0
语法和读取器
如果元素以相同的形式生成,则我不能使用字面量语法创建具有唯一成员/键的集合/映射。此类合法形式的示例:(rand)、(read)、(clojure.core.async/<!!)、等。我将使用(rand)在这些示例中。


user=> #{(rand) (rand)}
IllegalArgumentException 重复键:(rand) clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:68)

user=> {(rand) 1, (rand) 2}

IllegalArgumentException 重复键:(rand) clojure.lang.PersistentArrayMap.createWithCheck (PersistentArrayMap.java:70)


似乎在评估集合构造函数的参数之前就检查了重复项。然而,这并不能阻止以后再次进行检查。

注意,即使在重复项作为源中的字面量出现之前,复制也仍(正确)被检测到


user=> #{(+ 1 1) 2}

IllegalArgumentException 重复键:2  clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:56)
user=> {(+ 1 1) :a, 2 :b}

IllegalArgumentException 重复键:2  clojure.lang.PersistentArrayMap.createWithCheck (PersistentArrayMap.java:70)


因此,第一次重复项检查似乎既多余又错误。

请注意,这种急切的重复项检查似乎比语法引号读取宏的优先级更高。


user=> `#{~(rand) ~(rand)}

IllegalArgumentException 重复键:(clojure.core/unquote (rand)) clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:68)

user=> `{~(rand) 1, ~(rand) 2}

IllegalArgumentException 重复键:(clojure.core/unquote (rand)) clojure.lang.PersistentArrayMap.createWithCheck (PersistentArrayMap.java:70)


这很奇怪 —— 因为语法引号在读取时不应该实现任何集合。

bq. 对于列表/向量/集合/映射,语法引号为相应的数据结构建立一个模板。在模板内,未限定的形式表现得像递归语法引号,但形式可以通过使用限定的unquote或unquote-splicing来免除这种递归引号,在这种情况下,它们将被视为表达式并替换为模板中的值或值序列。(https://clojure.org/reader

不顾定义,基于语法引号看似的展开,我期待之前的操作能够正确执行。

通过对所需的输入进行手动替换,我得到了预期的结果


user=> '`#{~:a ~:b}
(clojure.core/apply clojure.core/hash-set (clojure.core/seq (clojure.core/concat (clojure.core/list :b) (clojure.core/list :a))))
user=> (clojure.core/apply clojure.core/hash-set (clojure.core/seq (clojure.core/concat (clojure.core/list (rand)) (clojure.core/list (rand)))))
#{0.27341896385866227 0.3051522362827035}
user=> '`{~:a 1, ~:b 2}
(clojure.core/apply clojure.core/hash-map (clojure.core/seq (clojure.core/concat (clojure.core/list :a) (clojure.core/list 1) (clojure.core/list :b) (clojure.core/list 2))))
user=> (clojure.core/apply clojure.core/hash-map (clojure.core/seq (clojure.core/concat (clojure.core/list (rand)) (clojure.core/list 1) (clojure.core/list (rand)) (clojure.core/list 2))))
{0.12476921225204185 2, 0.5807961046096718 1}


在我看来,在集合/映射的阅读宏计算参数之前,似乎正在进行一个多余的重复检查。这项检查似乎是应该被移除的。即使这项检查没有捕捉到一些错误阳性重复(它的确做到了),由于看起来第二次评估后的检查会捕获所有真正重复的内容,它也是不必要的。

尽管如此,不清楚这项检查是否应该发生。如果我尝试创建包含重复成员/键的集合/映射,我不会收到错误。重复内容会被静默删除或替代。


user=> (set (list 1 1))
#{1}
user=> (hash-map 1 2 1 3)
{1 3}


似乎对于由读取语法构造的文本文诸如同样一致。

我能理解这种字面表示并不是一个‘请求构造’,而是一种尝试模拟字面数据对象的打印表示。从这个角度来看,禁止‘非法’的打印表示似乎是合理的。不幸的是,由于列表在读取时就会评估,所以字面向量、集合和映射内部已经打破了这种理论。也就是说,这种集合的打印表示*不是*一个准确的可读形式,因此读取时的重复检查仍然不能防止打印/读取表示中的看似不一致的问题。


user=> '#{(+ 1 1) 2}
#{(+ 1 1) 2}
user=> #{(+ 1 1) 2}

IllegalArgumentException 重复键:2  clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:56)


由于问题不可能完全避免,因此将读取器字面构造器视为其运行时对应版本似乎是最简单、最一致的。正如语法引号在没有这种无用的重复检查时所做的。

3 个答案

0

是由 alexmiller 发表的评论

另请参阅 CLJ-1555

0

是由 bronsa 发表的评论

可能与http://dev.clojure.org/jira/browse/CLJ-1425相关

0
参考:https://clojure.atlassian.net/browse/CLJ-1538(由 alex+import 报告)
...