我在2.11.8
的scala(2.12.1
和Seq[(Long, Double)]
)中遇到了一些非常奇怪的排序行为。我可能误解了一些基本的东西。
给定一个没有Double.NaN
值的序列,一切都按预期工作
Seq[(Long, Double)]((1L, 2.5D), (2L, 0D), (11L, 11D), (2L, 10D)).sortWith(_._2 > _._2)
output >>> Seq[(Long, Double)] = List((11,11.0), (2,10.0), (1,2.5), (2,0.0))
如果我在排序列中添加一个带有NaN
的元组,则会发生奇怪的事情
Seq[(Long, Double)]((1L, 2.5D), (2L, 0D), (3L, Double.NaN), (11L, 11D), (2L, 10D)).sortWith(_._2 > _._2)
output >>> Seq[(Long, Double)] = List((1,2.5), (2,0.0), (5,NaN), (11,11.0), (2,10.0))
所以看起来没有做任何事情
如果我交换前两个元素
Seq[(Long, Double)]((2L, 0D), (1L, 2.5D), (5L, Double.NaN), (11L, 11D), (2L, 10D)).sortWith(_._2 > _._2)
output >>> Seq[(Long, Double)] = List((11,11.0), (2,10.0), (1,2.5), (2,0.0), (5,NaN))
再次排序?
仅使用Seq[Double]
Seq[Double](2.5, 0, Double.NaN, 11, 10).sortWith(_ > _)
output >>> Seq[Double] = List(2.5, 0.0, NaN, 11.0, 10.0)
Seq[Double](0, 2.5, Double.NaN, 11, 10).sortWith(_ > _)
output >>> Seq[Double] = List(11.0, 10.0, 2.5, 0.0, NaN)
.sortBy(_._2)
似乎适用于所有情况。这是scala或我脑中的错误吗?我在2.11.8
和2.12.1
上使用scala Ubuntu 16.04
和Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_91
。
更新
事实证明,如果我颠倒排序顺序,那么会发生更多可预测的事情
Seq[(Long, Double)]((1L, 2.5D), (2L, 0D), (3L, Double.NaN), (11L, 11D)).sortWith(_._2 < _._2)
output >>> Seq[(Long, Double)] = List((2,0.0), (1,2.5), (3,NaN), (11,11.0))
但是再次在中间添加NaN
会破坏排序
Seq[(Long, Double)] = List((2,0.0), (3,NaN), (1,2.5), (11,11.0))
output >>> Seq[(Long, Double)] = List((1,2.5), (3,NaN), (2,0.0), (11,11.0))
所以Seq.sortWith
或.sortBy
只是在看到NaN
时决定放弃?
在一个稍微不相关的说明中,当我试图对上面那种类型的元组进行排序时,spark
抛出了一个错误(下面),我遇到了这个问题。上面的结果仅来自scala
REPL,但那里没有任何火花。
java.lang.IllegalArgumentException:比较方法违反了其一般合同!
还有.max
和.min
与[{1}} s min/max of collections containing NaN (handling incomparability in ordering)
答案 0 :(得分:5)
Spark异常实际上是一个非常好的提示,关于这里发生了什么:Spark假设比较方法在输入集上定义了 Total Order ,这意味着,除其他外,对于集合中的每两个元素:
A > B OR B > A
现在,函数_ > _
是双打的总排序,但所有双打和NaN 的不是全部。使用这些简单的测试可以看出这一点:
scala> Double.NaN > 1.0
res19: Boolean = false
scala> Double.NaN < 1.0
res20: Boolean = false
因此,NaN
不大于也不小于1.0。
回到sortWith
的实现 - 我没有检查,但我假设它做出了同样的假设,但是当它不是这种情况时,不是抛出错误,而是简单地使其结果变得不可预测(顺序) - 依赖)当总体被打破时。
修改强>:
所以Seq.sortWith或.sortBy只是在看到NaN时决定放弃?
不,它只是返回“错误”的结果,因为它由于假设没有得到维护而做出错误的决定。没有测试正在寻找NaN
并放弃(正如@TheArchetypalPaul所评论的那样:“对此进行排序处理需要它在比较之前检查每个值都不是NaN。不值得它”)。