Я просто столкнулся с неприятной ошибкой в результате следующего поведения:
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 до вызова min/max. Поскольку это требует явного решения проблемы во всех местах и может привести к штрафам за производительность, я бы предпочел что-то более простое.
-
Было бы здорово иметь своего рода 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) } } }
Однако этот подход, похоже, зависит от того, интересно ли мне находить min или max, т.е.
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
. Поэтому мой вопрос: как я могу определить порядок для обработки обоих случаев сразу?
Update:
Ради полноты: во время анализа вопроса я понял, что предпосылка "вырождается до проверки для NaN" на самом деле ошибочна. На самом деле, я думаю, это еще более уродливо:
scala> List(1.0, Double.NaN).min
res1: Double = NaN
scala> List(Double.NaN, 1.0).min
res2: Double = 1.0