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

欢迎!请参阅关于页面以了解更多关于如何使用本网站的信息。

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`接口。下面是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文档中指向它。


这种合约应该有多大的直觉性取决于每个开发者的背景,这是一个非常主观的问题;从不同角度看:假设我可以从映射中获得任何成功放入的键对这似乎是完全合理的。更进一步,即使可以直观地认为所有键之间必须存在某种排序,以启用有序映射上的关联和获取操作,但能够在没有进一步文档的情况下关联不同类型的键,这会加强(不正确的)信念:默认的比较器确实能够为不同类型提供排序。
2023-10-25
我想要明确表示,我听到了您的话,理解了您的困惑和挫折!在文档中试图解决这个问题可能有所帮助,但我也预料到人们仍可能遇到类似的问题。我知道至少我自己确实如此,尽管我来自Java的背景。试图在一个拥有自身历史和不同哲学的庞大生态系统中构建一个一致、优秀的语言是一个巨大的挑战。
> 试图在一个拥有自身历史和不同哲学的庞大生态系统中构建一个一致、优秀的语言是一个巨大的挑战。

我完全同意,即使是在完全拥有两个系统的情况下,保持一致性也是一项挑战。Clojure能够在两个(或更多)具有如此长历史的生态系统中构建出一门一致的语言,这是对其深入思考的证明,我对此表示感谢。

1 个答案

0 投票
 

这是预期的行为(当在一个按比较器排序的Map中查找一个不处理查找键类型的键时,与Java的行为匹配)。


编辑
谢谢您的快速回复!
作为一个从Clojurescript入手Clojure生态系统的人,我没有多少Clojure和更少的Java背景,这真的很不直观(因为语言的其他部分设计得逻辑性强且旨在最小化意外)。

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

大概是这样的:“`sorted-map`允许您使用不同类型的键`assoc`,但不允许查找。这类似于Java在查找按比较器排序的Map中的键时不处理查找键类型时的行为。”
...