2017-02-22 61 views
2

我在scala(2.11.82.12.1)中遇到了一些非常奇怪的排序行爲Seq[(Long, Double)]。我可能誤解了一些根本性的東西。scala Seq sortWith or sortBy with NaNs

給定的順序沒有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只是決定放棄它時ees a NaN


在一個稍微不相關的說明,我碰到這個際,spark被扔(下)一個錯誤,當我嘗試理清上述類型的元組序列。以上結果僅來自scala REPL,但沒有涉及到火花。

java.lang.IllegalArgumentException:比較方法違反了它的一般合約!


也有一個與NaN小號min/max of collections containing NaN (handling incomparability in ordering)

+1

值得一提的是,下面的排序算法在'java.util.Arrays.sort'中。 –

回答

5

星火例外的.max.min一個相關的問題實際上是一個很好的提示,什麼是怎麼回事:星火假設的比較方法定義了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也不小於1.0。

回到sortWith的實現 - 我沒有檢查,但我假設它做出了相同的假設,但不是在不是這種情況時拋出錯誤,它的結果只是變得不可預知(與順序有關)當整體被打破。

編輯

所以Seq.sortWith或.sortBy剛剛決定放棄時,它看到了喃?

不,它只是返回「錯誤」的結果,因爲它做出錯誤的決定,由於假設不被支持。有沒有測試尋找NaN s並放棄(作爲@TheArchetypalPaul評論說:「排序處理這將需要它檢查每個值不是NaN比較之前,不值得)」。

+0

打我吧。我只是在輸入非常類似的東西! –

+0

我得到了可傳遞性部分,我仍然期望語言以一致的方式對待'NaN',也許根本不會改變它們的位置。另外,請注意,它不是'sortBy',而是'sortWith'。這使我們變成了因爲我使用'>'而不是'< - - 見上面的更新。 –

+0

這不只是傳遞性。排序假定總排序。因此對於所有的A和C來說,A <= C或C