请在 2024 年 Clojure 调查!(英文) 中分享您的想法!

欢迎!请参阅 关于 页面以获取更多有关本网站如何运作的信息。

0 投票
ClojureScript
已关闭

你好,
我遇到了一些关于 sorted-map 的奇怪行为,我认为这可能是一个bug或缺乏文档说明。

具体来说,当我按照如下方式创建一个使用不同类型键的排序映射时:(sorted-map :k1 :v1 "k2" :v2),然后进行如下查询:(:k1 (sorted-map :k1 :v1 "k2" :v2)),会导致异常:Cannot compare k2 to :k1。这看起来是 sorted-map 实现中使用 compare 的结果。

虽然完全支持异构键对我来说是最理想的做法,但似乎有理由要求排序映射的键是可比较的,但在我看来,这一点应该在 文档中 说明。

即使这将是一个要求,似乎更符合 Clojure 的通用设计精神,在查询时返回 nil,而不是抛出异常。我认为,在添加一个与已经存在于映射中的其他键不可比较的键时抛出异常会更合理。你觉得呢?

加油,
Simon

关闭原因:重复问题: 排序集合行为的某些困惑
在插入元素时尝试抛出异常需要在映射中扫描所有现有元素来验证它们是否可以与您要插入的元素进行比较,这将对性能和可扩展性造成巨大影响。同样,在引发比较并将异常转换为`nil`时捕获异常会添加开销,这将在映射的每项正确使用中浪费。这不是Clojure中唯一一个你必须了解所涉及数据结构的实际契约,并且必须正确使用否则会得到意外结果,以性能为代价的情况。(更不用说还需要创建所有底层Java类针对Clojure的特殊实现才能添加这些功能。)

在这个例子中,涉及到的数据结构是Java的`SortedMap`接口。以下是Java文档中的第二段内容:

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

随着你在JVM Clojure中深入了解,你不可避免地需要了解其构建于其上并与之交互的Java类库的细节。就此而言,归档链接以及可能将其总结/翻译成Clojure术语到Clojure文档字符串可能会有所帮助。
我可以理解导致行为如此的实际论点,并且尊重它们。

关于开发者体验和改进语言接纳的话题,尤其是关于Clojurescript,我会重申我回复Alex时提到的观点:对于主要使用Clojurescript的人来说,“Comparable” JVM契约的存在是相当隐秘且难以发现的。我建议从sorted-map文档中指向它。


我认为多少这样的契约应该被视为“直观的”是非常主观的,取决于每个开发者的背景;作为一个不同的观点:假设我可以从映射中退出我成功放入的每一对元素似乎完全是合理的。更进一步,即使可以直观地认为所有键之间必须存在某种顺序才能实现sorted map上的assoc和get,但由于缺少任何进一步的文档,能够将不同类型的键关联在一起的事实依然强化了(错误)信念,即默认的比较器确实能够在不同类型之间提供排序。
我希望明确的是,我听到您的话,理解了您的意思,并能感同身受您的困惑和挫折!在文档中试图解决这些问题可能会有所帮助,但我仍然期望人们在面对这类问题时会出错。我肯定我确实经常这么做,即使我拥有Java的背景。试图在一个拥有自己历史和发展,且包含不同哲学观点的庞大生态系统中构建一个一致且优秀的语言是一项艰巨的挑战。
> 试图在一个拥有自己历史和发展,且包含不同哲学观点的庞大生态系统中构建一个一致且优秀的语言是一项艰巨的挑战。

我完全同意,即使完全拥有两个系统,保持它们的一致性也很具挑战性。Clojure能够在拥有悠久传统和两个(或更多)生态系统的基础上构建一个一致的语言,这证明了其中的深度思考,我对此感到赞赏。

1 个答案

0 投票

这是预期行为(与Java在查找按比较器排序的Map中键时的行为一致)。


编辑
谢谢您的快速回复!
作为一个从Clojurescript进入Clojure生态系统的人,由于Clojure和Java的知识有限,这真的很不直观(因为语言的其他部分设计得很逻辑,旨在尽量减少意外)。

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

类似于:“sorted-map会允许您使用异构键进行关联,但不会允许查找。这类似于Java在查找一个由不处理查找键类型的比较器排序的Map中的键时的行为。”
...