为什么 F# 的惯用字典集合 (Map<K,V>) 需要类型 K 来实现可比性,而 C# 的字典 <K,V> 不需要?

Why F#'s idiomatic dictionary collection (Map<K,V>) needs the type K to implement comparable while C#'s Dictionary<K,V> doesn't?

我知道这个问题与我前一段时间问的这个问题非常相似:

但是,我想确认下给出的原因是否与这个案例相同?我想知道 F# 中是否存在由某人编写的不可变 Map 的实现,它不那么严格并且不需要 K 具有可比性?我很乐意使用它,因为我不太关心性能。

Why F#'s idiomatic dictionary collection Map<K,V> needs the type K to implement comparable while C#'s Dictionary<K,V> doesn't?

F# Map<Key, Value> 要求keys是可以比较的,因为map是implemented as a tree structure(你得根据比较结果来决定去哪一棵子树)。 C# Dictionary<Key, Value> 被实现为一个链表桶。你通过键的哈希码得到一个桶,然后迭代列表直到你(不)找到相同的键。在这两种数据结构中,都会比较键。唯一的区别是字典相等比较就足够了。

所以,问题是为什么 F# Mapexplicit 比较约束,而 C# Dictionaryimplicit 平等要求?

让我们从 C# 开始。如果字典有一个 IEquatable 键约束会怎样?好吧,您 必须为每个用作字典键的自定义数据类型手动实现此接口 。但是如果你想要平等的不同实现呢?例如。在某些词典中,您希望键字符串不区分大小写。当然,您可以传递 IEqualityComparer 实现以用于键比较(不仅对字典,而且在需要比较的任何地方)。但是,如果将使用外部比较器,为什么还要强制 key 具有可比性呢?请注意,如果您不向字典传递任何内容,则始终使用默认比较器。默认比较器检查键是否实现 IComparable 并使用该实现。

为什么 F# 对键数据类型有显式的可比较约束?因为此约束 不会强制您为用作映射键的每个自定义数据类型手动实施 IComparable。 C# 和 F# 类型系统之间的一大区别是默认情况下 F# 类型可比较和等同。 F# 编译器生成 IComparableIComparable<T>IStructuralComparable 实现,除非您使用 NoComparison 属性显式标记类型。因此,此约束不会强制您在使用 F# 数据类型时编写任何其他代码。

使用 comparison/equality 约束的另一个好处 - F# 对实现比较或相等的类型有许多预定义的泛型操作 (=、<、<=、>=、=、max、min)。这使得具有通用 comparable/equatable 类型的代码更具可读性。