我在scala(2.11.8和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))
Run Code Online (Sandbox Code Playgroud)
如果我NaN在排序列中添加一个带有a的元组,就会发生奇怪的事情
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))
Run Code Online (Sandbox Code Playgroud)
所以看起来什么都没做
如果我交换前两个元素
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))
Run Code Online (Sandbox Code Playgroud)
排序再次工作?
只是观察到同样的情况 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)
Run Code Online (Sandbox Code Playgroud)
.sortBy(_._2)似乎在所有情况下都有效.这是scala或我脑中的错误吗?我使用的斯卡拉2.11.8和2.12.1上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))
Run Code Online (Sandbox Code Playgroud)
但再次添加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))
Run Code Online (Sandbox Code Playgroud)
那么Seq.sortWith或者.sortBy只是决定在看到它时放弃NaN?
在稍微不相关的说明中,spark当我试图对上面那种类型的元组进行排序时,我遇到了这个错误(下面).上面的结果只是来自scalaREPL,但那里没有任何火花.
java.lang.IllegalArgumentException:比较方法违反了它的一般合同!
还有一个相关的问题上.max,并.min用NaN小号最小/最大含有NaN的集合(处理订货不可比性)
Spark异常实际上是一个非常好的提示,关于这里发生了什么:Spark假设比较方法在输入集上定义了一个Total Order,这意味着,除了其他之外,对于集合中的每两个元素:
A > B OR B > A
Run Code Online (Sandbox Code Playgroud)
现在,该功能_ > _是双打总订单,但它是不是总在一组的所有双打和NaN的.这可以通过以下简单测试看出:
scala> Double.NaN > 1.0
res19: Boolean = false
scala> Double.NaN < 1.0
res20: Boolean = false
Run Code Online (Sandbox Code Playgroud)
所以,NaN不大于也不小于1.0.
回到实现sortWith- 我没有检查,但我假设它做出相同的假设,但是当它不是这种情况时,它的结果只是在整体性被破坏时变得不可预测(依赖于顺序)而不是抛出错误.
编辑:
所以Seq.sortWith或.sortBy只是在看到NaN时决定放弃?
不,它只是返回"错误"的结果,因为它做出了错误的决定,因为假设没有得到维护.没有测试寻找NaN和放弃(正如@TheArchetypalPaul所评论的那样:"对此进行排序处理将要求它在比较之前检查每个值不是NaN.不值得").