Представьте себе: это вторая неделя учебного курса по программной инженерии в Flatiron School, и я уже 55 минут занимаюсь программированием на Ruby с 60-минутным ограничением по времени.

Я пытался создать метод, который возвращал бы среднее значение набора данных, состоящего как из чисел, так и из nil значений. В поспешной попытке обойти значения nil я закодировал себя в ситуации, когда метод в некоторых случаях заканчивался попыткой разделить ноль на ноль. В частности, метод деления 0 на 0,0.

Что происходит, когда вы пытаетесь разделить на ноль? Википедия, просветите нас:

В вычислении программная ошибка может возникнуть из-за попытки деления на ноль. В зависимости от среды программирования и типа числа (например, с плавающей запятой, целое) при делении на ноль может генерировать положительную или отрицательную бесконечность по стандарту IEEE 754 с плавающей запятой, генерировать исключение , генерировать сообщение об ошибке, вызывать завершение программы, приводить к специальному значению не число, зависанию через бесконечный цикл или сбою.

Какой восхитительный набор опций!! Итак, кого из этих победителей должен сгенерировать мой код?

Поскольку меня учили самые лучшие здесь, во Flatiron, я сразу же проверил свой метод, чтобы выяснить…

и вызов метода, который привел к попытке разделить 0 на 0,0, вернул следующее: NaN

Не та Нэн, ЭТО NaN

NaN, как вы могли логически заключить, означает не число. Это член числового типа данных — точнее, это экземпляр класса Float (Float::NAN), и его значение не определено. Полезный.

Если бы этот результат, #average_rating, был самостоятельным, я, возможно, сошёл бы с рук. (Примечание редактора: ей бы это не сошло с рук.) Но, конечно же, я собирался вызывать метод #average_rating в отдельном методе, поэтому пришлось иметь дело с NaN.

Боковая панель 1: Если вы думаете про себя: «Не было бы лучше реорганизовать ваш код, чтобы избежать любой ситуации, когда вы могли бы попытаться делить на ноль?» Что ж, 20 баллов Рейвенкло, но я из Хаффлпаффа, и мы ценим верность, поэтому я остался с моим дорогим маленьким другом NaN и продвигался вперед. (Также я могу напомнить вам, что до конца испытания осталось 5 минут, и ни у кого нет времени на рефакторинг, когда у вас все еще есть незаконченные результаты.)

Моей первой мыслью было, что NaN оценивается как ложное, и поэтому я мог бы настроить условное выражение, чтобы игнорировать любое ложное возвращение.

Что ж, моей первой мыслью было НЕПРАВИЛЬНО, потому что NaN на самом деле оценивается как правдивый.

Почему он оценивается как правдивый? Короткий ответ: у Ruby есть только 2 ложных значения: false и nil. Более длинная причина связана с инженерно-математическим консорциумом и техническими стандартами, которые мой старый гуманитарный мозг не мог разобрать, но, похоже, это как-то связано со значениями с плавающей запятой. (См. Стандарт IEEE для арифметики с плавающей запятой (IEEE 754) для получения дополнительной информации.)

Врезка 2: Тот факт, что я работал с Float, важен. Попытка разделить Integer на 0 на самом деле возвращает 🙌ПОЛЕЗНО🙌ZeroDivisionError. Но попытка разделить на 0,0 в Float земле вернет особое значение, либо NaN (Float::NAN), если вы пытаетесь разделить 0,0/0,0, либо Infinity (Float::INFINITY), если вы пытаетесь разделить любой другое положительное или отрицательное число с плавающей запятой на 0,0

25 / 0
#=> ZeroDivisionError (divided by 0)
0 / 0
#=> ZeroDivisionError (divided by 0)
25 / 0.0
#=> Infinity
-25 / 0.0
#=> Infinity
0/0.0
#=> NaN

Боковая панель 3: NaN является ложным в Javascript. Этот факт никоим образом не помог моему коду на Ruby, но заставил меня почувствовать, что я не ПОЛНОСТЬЮ заблуждался, предполагая, что это будет ложно.

К счастью, в Ruby есть метод, встроенный в класс Float, который может нам здесь помочь:

nan?

Я имею в виду, конечно, она делает. Это Руби. Она любит нам помогать. Из документации:

С помощью этого метода я мог установить условие, чтобы игнорировать любые значения NaN-y, которые могут быть возвращены при вызове метода #average_rating.

Подводя итог и заключая: NaN означает не число. Используйте nan? метод, чтобы обойти любые потенциальные NaN значения. Или, что еще лучше, реорганизуйте свой код, чтобы избежать деления на 0 (будь то Float или Integer).