请分享您的想法,参加2024Clojure状态调查!

欢迎!请参阅关于页面,了解更多有关此内容的详细信息。

0
ClojureScript
已关闭

你好,
我遇到了一些关于sorted-map的奇怪行为,对我来说看起来像是一个错误或缺乏文档。

具体来说,当创建一个具有不同类型键的有序映射,如下所示:(sorted-map :k1 :v1 "k2" :v2)然后进行如下查找:(:k1 (sorted-map :k1 :v1 "k2" :v2))会产生异常:无法比较k2与:k1。这似乎是由于sorted-map实现中使用了compare

虽然全支持异构键(对我而言)是一个更合适的做法,但似乎要求sorted-map的键是可比较的更有道理,但据我所知,这一点应在文档中声明。

即使这将成为一个要求,但在我看来,这更符合Clojure的一般设计精神,当进行此类查找时返回nil,而不是抛出异常。对我来说,在添加一个与已存在的键不可比的键时抛出异常更有意义。你有什么看法?

祝好,
Simon

被关闭,因为它是以下问题的重复:有点令人困惑的sorted-set行为
在插入元素时尝试抛出异常,需要扫描图中所有现有元素以验证它们是否与您要插入的元素可比较,这对性能和可扩展性会产生极大的负面影响。同样,在您强制进行比较并将其转换为 `nil` 时捕获异常,也会添加额外的开销,这在正确使用映射时会被浪费。这不是 Clojure 中需要理解涉及的实际契约并且正确使用它们才能得到预期结果的唯一情况,否则可能会因为性能而得到意想不到的结果。(更不用说还需要特殊的 Clojure 面向所有基础 Java 类的实现来添加这些功能。)

在这种情况下,相关数据结构是 Java 的 `SortedMap` 接口。以下是可以从 JavaDoc 的第二段看到的内容:

所有插入到排序映射中的键都必须实现 `Comparable` 接口(或者被指定的比较器接受)。此外,所有此类键都必须彼此可比较:`k1.compareTo(k2)`(或 `comparator.compare(k1, k2)`)不能为排序映射中的任何键 `k1` 和 `k2` 抛出 `ClassCastException`。违反此限制的尝试将导致引发异常的方法或构造函数调用抛出 `ClassCastException`。

随着您对 JVM Clojure 的深入了解,不可避免地需要学习它所构建和互操作的 Java 类库的细节。因此,在 Clojure 的文档字符串中提供一个指向此 JavaDoc 的链接,以及可能将其总结/翻译成 Clojure 术语,可能会非常有用。
我可以欣赏那些导致的行为如此的实际论点,并且尊重它们。

关于开发者体验和改进语言采用,特别是关于 clojurescript,我会在对 Alex 的回复中提到的观点:存在的“Comparable” JVM 协议相当隐秘,并且对主要从事 Clojurescript 的人来说很难发现。我建议从 sorted-map 文档中指向它。


我认为这样的协议应该有多“直观”非常主观,取决于每个开发者的背景;从不同的角度来看:假设我能够从映射中获取到每个成功放入的键值对是完全合理的。更进一步来说,即使可以直观地认识到所有键之间必须有一种排序以便在排序映射上启用 assoc 和 get,但在没有进一步文档的情况下,能够将不同类型的键关联起来,也增强了(不正确的)信念,即默认的比较器确实能够在不同类型之间提供排序。
我想明确表示我听到了您的话,理解您,并同情您的困惑和挫败感!在文档中尝试预防这些建议可能会帮助,但我仍然预期人们会在诸如此类的事情上遇到麻烦。我知道,尽管我来自Java的背景,但我肯定已经,有时确实遇到了这种情况。在拥有自己的历史和不同哲学的庞大生态系统中建立一个一致、优秀的语言是一个巨大的挑战。
> 在拥有自己的历史和不同哲学的庞大生态系统中建立一个一致、优秀的语言是一个巨大的挑战。

完全同意,即使是维护两个完全拥有的系统之间的连贯性也有时会很具挑战性。Clojure能够在上面的两个(或更多)具有悠久历史的生态系统之上建立一个一致的语言,这是对其中深思熟虑的证明,我对此表示赞赏。

1 答案

0

这是预期行为(与在通过不处理查找键类型的比较器对Map进行排序时查找键的行为一致)。


编辑
谢谢您迅速的回答!
作为从Clojurescript进入Clojure生态的人,没有多少Clojure甚至更少的Java背景,这真的不太直观(鉴于语言的其他部分设计逻辑并且旨在减少意外)。

您考虑在sorted-map文档中解释这种行为吗?

例如:“sorted-map将允许您关联异构键,但不会允许查找。这符合在通过不处理查找键类型的比较器对Map进行排序时查找键的Java行为。”
...