scala Seq sortWith或sortBy with NaNs

时间:2017-02-22 14:33:05

标签: scala sorting

我在2.11.8的scala(2.12.1Seq[(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.82.12.1上使用scala Ubuntu 16.04Java 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)

相关的问题

1 个答案:

答案 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。不值得它”)。