2015-02-06 11 views
19

Я не могу понять следующее поведение диапазонов в Haskell. Перечисление с 1 по 1 дает мне список, содержащий только 1, и от 2 до 2 дает мне список, содержащий только 2, как указано ниже.Если я нахожу [1/0..1/0] в GHCI, я получаю Бесконечную Бесконечность. Зачем?

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

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

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

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

+8

Это на самом деле более разумно, чем многие другие последствия значений нереального числа IEEE754. – leftaroundabout

ответ

31

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

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

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

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

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

+0

Извините за случайный downvote-я удалю его, как только он больше не заперт. – Slade

+0

@Slade: Ах, не проблема. Я добавил ссылку на некоторое чтение, поэтому ваш голос не должен быть заблокирован. –

3

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

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

13

Как Луис Вассерман и Тихон Jelvis показал, основная проблема заключается в том, что Num и Enum экземпляров для Float и Double являются странных, и на самом деле не существует. На самом деле сам класс Enum довольно странный, поскольку он пытается одновременно обслуживать сразу несколько различных целей, но ни один из них не является хорошим - это, вероятно, лучше всего считать исторической катастрофой и удобством, а не хорошим примером того, как выглядит классная панель , То же самое можно сказать и о классах и 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, включая числовые диапазоны, с плавающей запятой.

+0

succ. Именно по этой причине в Frege нет «Enum Double» или «Enum Float». – Ingo