为什么 PartialEq 返回一个布尔值,当 PartialOrd 更细微并返回 Option<Ordering> 时?

问题描述

这看起来是不对称和基本的,所以我想了解其中的原因,为什么PartialOrd定义了partial_cmp,返回Option<Ordering>

fn partial_cmp(&self,other: &Rhs) -> Option<Ordering> {}

PartialEq 定义 eq 返回 bool

fn eq(&self,other: &Rhs) -> bool {}

为什么 PartialEq::eq 不返回 Option<Eq>,其中 None 代表无法确定相等的事物?为什么 PartialEq 不知道哪些值不能比较相等,从而阻止了完整的 Eq trait 的实现,而 PartialOrd 必须知道哪些值可以不能比较完整的排序(因此它可以将该信息作为 None 返回给用户)?

解决方法

我能想到一些为什么这些特征被定义为它们的方式的原因:

  1. PartialEq== 运算符的特征。如果要返回 Option,则至少需要重新考虑围绕 if 和其他控制流的语言人体工程学。

  2. 在考虑值是否相等时,“不可比较”的值属于后一类。不需要第三个答案;要么他们是平等的,要么不是。这对于 > 提供的 >=<<=PartialOrd 是相同的,它们都返回 bool,无论值是否为“不可比较”(在这种情况下它们返回 false)。

  3. 类型中“部分”的含义不同。 PartialOrd 中的“部分”表示类型可能没有 total order。而 PartialEq 中的“部分”表示类型可能没有完整的 equivalence relations(可能不是自反、对称或传递)。命名是相似的,因为它们都涵盖了类型表现不佳的情况,但它们传达的概念略有不同。 API 不需要完全相同。

,

因为按照数学标准,PartialOrd 有点作弊。

抽象地说,数学结构由两件事定义:一些运算和与这些运算相关的一些公理。在编程语言中,我们可以定义体现数学结构的特征(或类型类,或任何您喜欢的语言对它们的称呼)。我们定义 trait 来支持某些操作,并简单地相信程序员遵循公理。

Total orderings(即Ord)和partial orderings(即PartialOrd)通常使用单个操作建模:<=。并且这两种结构的区别是基于我们对 <= 的要求。同样,partial equivalence relations (PartialEq) 和 equivalence relations (Eq) 都是根据 == 定义的,两者的区别仅在于哪些属性。

如果我们在 Rust 中以这种方式定义 PartialOrdOrd,它看起来就像(为了简单起见,我省略了 RHS 类型参数)

trait PartialOrd : PartialEq {
  fn less_or_equal(self,other: Self) -> bool;
}
trait Ord : PartialOrd {}

这看起来更像是 PartialEq / Eq 二分法的样子。 Rust 选择根据另一个运算符实现 PartialOrd:三分运算符 partial_cmpcmp。在任何总排序中,我们可以定义一个运算符 cmp(a,b),它返回 a 是小于、等于还是大于 b。在总排序中,其中一个是正确的。然而,在部分排序中,我们仍然可以定义这个运算符 partial_cmp(a,b),但还有第四种选择,即两者根本没有可比性。 >

至于 Rust 为什么这样做,我不能肯定。 Haskell 有一个类似的设置,有一个 Ord 类型类,在 Haskell 中你可以实现 <=compare,你可以免费获得另一个。 Python 曾经使用 __cmp__(基本上是 Rust 的 cmp),但 Python 3 完全切换到定义 <= 并使用 functools.total_ordering 进行比较。

我们可以反复讨论哪种机制更好。 cmp 在清晰度和对称性方面有其优势,但 <= 更适合学习抽象数学。如果您以数学方式定义这两种结构(==PartialEq<=PartialOrd),那么在这两种情况下,您都会得到一个“返回”{{ 1}},所有其他差异都在公理中。我们在 Rust 中没有那种令人满意的对称性的原因是 Rust 的 bool 是根据一个运算符定义的,它只在 PartialOrd 中具有完全的数学意义;我们采用了一个总排序运算符并删除了它的一些分支,留下一个只能在“某些时候”工作的运算符。