Подтвердить что ты не робот

Если я введу [1/0..1/0] в GHCI, я получаю Бесконечную Бесконечность. Зачем?

Я не могу понять следующее поведение диапазонов в Haskell. Перечисление с 1 по 1 дает мне список, содержащий только 1, и от 2 до 2 дает мне список, содержащий только 2, как указано ниже.

Prelude> [1..1]
[1]
Prelude> [2..2]
[2]

Но перечисление бесконечности в бесконечность дает мне список, который бесконечен по длине, и все элементы бесконечны, как показано ниже.

Prelude> [1/0..1/0]
[Infinity,Infinity,Infinity,Infinity,Infinity,Infinity,Interrupted.

Я знаю, что Infinity - это понятие, которое нельзя рассматривать как число, но что оправдывает это поведение?

4b9b3361

Ответ 1

Haskell Double (по умолчанию вы получаете при использовании /) следует стандарт IEEE 754 для чисел с плавающей запятой, который определяет как Infinity ведет себя. Вот почему 1/0 - Infinity.

По этому стандарту (и, если быть справедливым, по логике), Infinity + 1 == Infinity, а экземпляр Enum для Double просто добавляет 1 каждый раз.

Это еще один признак того, что экземпляр Enum для Double не совсем корректно сформирован и не соответствует ожиданиям, которые мы обычно имеем для экземпляров Enum. Поэтому, как правило, вам, вероятно, следует избегать использования .. для чисел с плавающей запятой: даже если вы понимаете, как это работает, это будет путать других, читающих ваш код. В качестве еще одного примера запутанного поведения рассмотрим:

Prelude> [0.1..1]
[0.1,1.1]

Если вы хотите получить более подробные сведения о экземпляре Enum для Double, вы можете прочитать это довольно длинное Haskell-cafe нить.

Ответ 2

Как указали Луис Вассерман и Тихон Джелвис, основная проблема заключается в том, что экземпляры Num и Enum для Float и Double являются странными и на самом деле не должны существовать вообще. На самом деле сам класс Enum довольно странный, так как он пытается одновременно обслуживать сразу несколько разных целей, но ни один из них не стоит хорошо - вероятно, лучше всего считать историческую катастрофу и удобство, а не хороший пример того, что класс выглядит как. То же самое в значительной степени относится к классам Num и Integral. При использовании любого из этих классов вы должны уделять пристальное внимание конкретным типам, над которыми вы работаете.

Методы enumFromTo для плавающей запятой основаны на следующей функции:

numericEnumFromTo :: (Ord a, Fractional a) => a -> a -> [a]
numericEnumFromTo n m = takeWhile (<= m + 1/2) (numericEnumFrom n)

Итак, 2 <= 1+1/2 является ложным, но из-за странности плавающей запятой infinity <= infinity + 1/2 является истинным.

Как указывает Тихон Джелвис, обычно лучше не использовать какие-либо методы Enum, включая числовые диапазоны, с плавающей точкой.

Ответ 3

Я полагаю, что реализация [a..b] продолжает увеличивать a на единицу до тех пор, пока она не станет больше b. Это никогда не произойдет с бесконечностью, так что это происходит навсегда.

Я полагаю, что ваш код, вероятно, по умолчанию будет Double тем, как вы его написали, который имеет четко определенную семантику для бесконечности. IIRC, Haskell следует http://en.wikipedia.org/wiki/IEEE_floating_point.