мин / макс коллекций, содержащих NaN (обработка несопоставимости при заказе)
Я только что столкнулся с неприятной ошибкой в результате следующего поведения:
scala> List(1.0, 2.0, 3.0, Double.NaN).min
res1: Double = NaN
scala> List(1.0, 2.0, 3.0, Double.NaN).max
res2: Double = NaN
Я понимаю, что для парного сравнения иногда может быть предпочтительнее иметьmax(NaN, 0) = NaN
и это, вероятно, причина, почемуjava.lang.Double.compare
следует этому соглашению (кажется,Стандарт IEEE для этого). Однако для коллекции я действительно считаю, что это странное соглашение. Ведь вышеупомянутая коллекция действительно содержит действительные числа; и эти числа имеют четкий максимум и минимум. На мой взгляд, концепция о том, чтомаксимальное количество из коллекциине число противоречие, так как NaN не является числом, поэтому оно не может быть максимальным или минимальным «числом» коллекции - если только нет действительных чисел; в этом случае имеет смысл, что максимум "не число". Семантическиmin
а такжеmax
функции вырождаются, чтобы проверить, содержит ли коллекция NaN. Поскольку существуют более подходящие способы проверить наличие NaN (например,collection.find(_.isNaN)
) было бы здорово поддерживать семантически значимый минимум / максимум в коллекциях.
Итак, мой вопрос: каков наилучший подход для получения поведения, позволяющего игнорировать существование NaN? Я вижу две возможности:
Фильтрация NaN перед вызовом мин / макс. Поскольку это требует явного решения проблемы во всех местах и может повлечь за собой снижение производительности, я бы предпочел что-то более простое.
Было бы замечательно иметь порядок упорядочения, игнорирующий NaN, который можно использовать как неявное упорядочение там, где это необходимо. Я попробовал следующее:
object NanAwareOrdering extends Ordering[Double] {
def compare(x: Double, y: Double) = {
if (x.isNaN()) {
+1 // without checking x, return y < x
} else if (y.isNaN()) {
-1 // without checking y, return x < y
} else {
java.lang.Double.compare(x, y)
}
}
}
Тем не менее, этот подход, кажется, зависит от того, заинтересован ли я в поиске минимума или максимума, т.е.
scala> List(1.0, 2.0, 3.0, Double.NaN).min(NanAwareOrdering)
res7: Double = 1.0
scala> List(1.0, 2.0, 3.0, Double.NaN).max(NanAwareOrdering)
res8: Double = NaN
Это означает, что у меня должно быть два NanAwareOrdering в зависимости от того, хочу ли я минимум или максимум, что запретило бы иметьimplicit val
, Поэтому мой вопрос: как я могу определить порядок таким образом, чтобы обрабатывать оба случая одновременно?
Обновить:
Ради полноты: в ходе анализа проблемы я понял, что посылка «вырождается в проверку на NaN» на самом деле неверна. На самом деле, я думаю, что это еще страшнее
scala> List(1.0, Double.NaN).min
res1: Double = NaN
scala> List(Double.NaN, 1.0).min
res2: Double = 1.0