haskell - Si ingreso[1/0..1/0] en GHCI, obtengo Infinite Infinity. ¿Por qué?
(3)
No puedo entender el siguiente comportamiento de rangos en Haskell. Enumerar 1 a 1 me da una lista que contiene solo 1, y 2 a 2 me da una lista que contiene solo 2 como se indica a continuación.
Prelude> [1..1]
[1]
Prelude> [2..2]
[2]
Pero la enumeración de infinito a infinito me da una lista de longitud infinita y todos los elementos son infinitos, como se muestra a continuación.
Prelude> [1/0..1/0]
[Infinity,Infinity,Infinity,Infinity,Infinity,Infinity,Interrupted.
Soy consciente de que Infinito es un concepto que no debe tratarse como un número, pero ¿qué justifica ese comportamiento?
Como lo han indicado Luis Wasserman y Tikhon Jelvis, el problema básico es que las instancias de Num
y Enum
para Float
y Double
son extrañas , y realmente no deberían existir en absoluto. De hecho, la clase Enum
sí misma es bastante extraña, ya que intenta cumplir varios propósitos diferentes a la vez, ninguno de ellos bien. Probablemente se considere un accidente histórico y una conveniencia en lugar de un buen ejemplo de cómo debería ser una clase de tipos. . Lo mismo es cierto, en un grado significativo, de las clases Num
e Integral
. Al usar cualquiera de estas clases, debe prestar mucha atención a los tipos específicos en los que está operando.
Los métodos enumFromTo
para punto flotante se basan en la siguiente función:
numericEnumFromTo :: (Ord a, Fractional a) => a -> a -> [a]
numericEnumFromTo n m = takeWhile (<= m + 1/2) (numericEnumFrom n)
Entonces 2 <= 1+1/2
infinity <= infinity + 1/2
es falso , pero debido a la rareza del punto flotante, infinity <= infinity + 1/2
es verdadero .
Como indica Tikhon Jelvis, generalmente es mejor no usar ningún método Enum
, incluidos los rangos numéricos, con punto flotante.
Creo que la implementación de [a..b]
continúa incrementando a
por uno hasta que es mayor que b
. Esto nunca va a pasar con el infinito, por lo que es para siempre.
Creo que su código probablemente se Double
la forma en que lo ha escrito, que tiene una semántica bien definida para el infinito. IIRC, Haskell sigue http://en.wikipedia.org/wiki/IEEE_floating_point .
Haskell''s Double
(el valor predeterminado que obtiene al usar /
) sigue el estándar IEEE 754 para los números de punto flotante, que define cómo se comporta Infinity
. Es por esto que 1/0
es el Infinity
.
Por este estándar (y, para ser justos, por lógica), Infinity + 1 == Infinity
, y la instancia Enum
para Double
solo agrega 1
cada vez.
Esta es otra señal de que la instancia de Enum
para Double
no está completamente bien formada y no se ajusta a las expectativas que normalmente tenemos para las instancias de Enum
. Por lo tanto, como regla general, probablemente debería evitar usar ..
para los números de punto flotante: incluso si entiende cómo funciona, será confuso para otros que lean su código. Como otro ejemplo de comportamiento confuso, considere:
Prelude> [0.1..1]
[0.1,1.1]
Si desea obtener muchos más detalles sobre la instancia de Enum
para Double
, puede leer este hilo bastante largo de Haskell-cafe .