请在 2024 Clojure 状态调查! 分享您的想法。

欢迎!有关如何使用本站的更多信息,请查阅 关于 页面。

0
ClojureScript
已关闭

你好,
我在使用 sorted-map 时遇到了一些奇怪的行为,似乎是一个bug或文档描述不足。

具体来说,在创建一个具有不同类型键的排序映射,如下所示: (sorted-map :k1 :v1 "k2" :v2),然后进行查找操作 (:k1 (sorted-map :k1 :v1 "k2" :v2)) 会抛出异常: 无法比较 k2 与 :k1。这看起来是由于 sorted-map 实现中使用 compare 所致。

尽管尽可能地完全支持不同的键可能是我更愿意选择的方案,但似乎让排序映射的键可比较是合理的,但在我看来,这一点应该在 文档 中明确说明。

即使这确实是一个要求,但似乎更符合 Clojure 的一般设计理念,在查找时返回 nil,而不是抛出异常。我认为应该在添加无法与其他键比较大小的键时抛出异常。您怎么看?

问候,
Simon

此问题被视为已关闭的重复问题: 有一定困惑的 sorted-set 行为
在插入元素时尝试抛出异常,需要遍历地图中所有现有元素来验证它们是否可以与您要插入的元素进行比较,这对性能和可扩展性非常不利。同样,在您引发比较并把它转换为 `nil` 时捕获异常,会增加不必要的工作负担,这会对正确使用地图的每次操作造成浪费。这不是 Clojure 中唯一的例子,你需要理解涉及到的数据结构的实际契约,并正确使用它们,否则可能会得到意料之外的输出,损害性能。(更不用说为了添加这些功能,还需要特殊的针对 Clojure 的 Java 类实现。)

在此情况下,涉及到的数据结构是 Java 的 `SortedMap` 接口。以下是 Java 文档第二段的内容:

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

随着你对 JVM Clojure 的深入了解,你不可避免地需要了解它所基于和交互操作的那些 Java 类库的详细信息。话虽如此,链接到这个 Java 文档,以及可能将其总结或翻译成 Clojure 术语,在 Clojure 文档字符串中可能是有用的。
by
我非常欣赏导致这种行为得到的实际论据,并且尊重它们。

关于开发者体验和改进语言采用,特别是与 ClojureScript 相关的部分,我会重申我在回复 Alex 的回复中提到的观点:存在“Comparable” JVM 合约这一事实对主要从事 ClojureScript 的人来说相当隐晦,而且很难发现。我建议从排序映射文档中指明这一点。


我认为这样一个契约应该被视为“直观”的程度很大程度上是主观的,取决于每个开发者的背景;从另一个角度来看:假设我可以从映射中成功获取每一对我放入的元素,这看来是完全合理的。更进一步,即使一个人可以直观地认为必须在所有键之间可能的排序,以便在排序映射上启用 `assoc` 和 `get`,但从没有进一步的文档来看,能够将不同类型的键 `assoc` 到映射中就强化了(不正确)的观点,默认比较器确实能够提供不同类型之间的顺序。
by
我想明确表示,我已经听到了您的话,理解了您的意思,并且对您的困惑和挫败感表示同情!争取在文档中解决问题将有望有所帮助,但我仍然预计人们会遇到像这样的问题。我知道,尽管我有Java背景,但我在某些时候还是会遇到这种情况。试图在拥有自身历史和不同哲学的庞大生态系统中共存,打造一个统一、优秀的语言是一项巨大的挑战。
> 尝试在拥有自身历史和不同哲学的庞大生态系统中共存,打造一个统一、优秀的语言是一项巨大的挑战。

完全同意,即使是保持一个完全控制的两个系统之间的统一性也是有挑战的。Clojure能够在两个(或更多)长有历史的生态系统中构建一个统一的语言,这是对其进行深入思考的证明,我对此表示赞赏。

1 个回答

0

这是预期的行为(与Java在通过不处理查找键类型的comparator对map进行排序时查找键的行为相同)。


编辑
谢谢快速的回答!
作为一个从Clojurescript进入Clojure生态系统的人,我没有太多的Clojure和Java背景,这在其他时候语言设计是逻辑性的,并且旨在最小化意外的情况下,这真是不直观的。

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

大致如下,“sorted-map将允许您使用异构键`assoc`,但不允许查询。这与Java在通过不处理查找键类型的comparator对map进行排序时查找键的行为相符)。
...