【问题标题】:scala Seq sortWith or sortBy with NaNsscala Seq sortWith 或 sortBy 与 NaN
【发布时间】:2017-02-22 14:33:05
【问题描述】:

我在 Scala 中遇到了一些非常奇怪的排序行为(2.11.82.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中的错误,还是我的大脑中的错误?我在 Ubuntu 16.04Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_91 上使用 scala 2.11.82.12.1

更新

事实证明,如果我颠倒排序顺序,那么会发生一些更可预测的事情

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 上还有一个相关问题NaNs min/max of collections containing NaN (handling incomparability in ordering)

【问题讨论】:

  • 不管怎样,下面的排序算法在java.util.Arrays.sort内。

标签: scala sorting


【解决方案1】:

Spark 异常实际上是对这里发生的事情的一个很好的提示:Spark 假设比较方法在输入集上定义了一个 Total Order,这意味着除其他外,对于集合中的每两个元素:

A > B OR B > A

现在,函数 _ &gt; _ 是 Doubles 的 Total Order,但它不是全部所有 Doubles 和 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 时决定放弃?

不,它只是返回“错误”的结果,因为它做出了错误的决定,因为假设没有得到支持。没有测试 寻找NaNs 并放弃(正如@TheArchetypalPaul 评论的那样:“进行排序处理需要在比较之前检查每个值不是NaN。不值得” )。

【讨论】:

  • 打败我。我只是在输入非常相似的内容!
  • 我得到了传递性部分,我仍然希望语言以一致的方式对待NaNs,也许根本不改变他们的立场。另外,请注意,损坏的不是sortBy,而是sortWith。这变成了我们,因为我使用的是 &gt; 而不是 &lt; - 请参阅上面的更新。
  • 这不仅仅是传递性。排序假定总排序。因此,对于所有 A 和 C,A
猜你喜欢
  • 2014-06-28
  • 1970-01-01
  • 2014-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多