tipos opciones listas hacer funciones ficheros ejercicios ejemplos datos como ciclos haskell floating-point range

opciones - Usando rangos de Haskell: ¿Por qué mapear una función de coma flotante a través de un rango hace que devuelva un elemento extra?



string en haskell (1)

Respondí esto por mí mismo mientras lo escribía.

Haskell utiliza la inferencia de tipo, de modo que cuando ve una función de coma flotante mapeada sobre una lista (o usada en un elemento de esa lista, como en mi ejemplo usando la última), va a inferir el tipo de esa lista como punto flotante y por lo tanto, evalúe el rango como si fuera [1,3..10] :: [Float] lugar de lo que yo pretendía, que es [1,3..10] :: [Int]

En este punto, usa las reglas de Float para enumerar, como se describe en la publicación a la que me he vinculado en la pregunta .

El comportamiento esperado se puede forzar así:

ghci> map (/x -> (fromIntegral x) / 10) ([1,3..10]::[Int]) [0.1,0.3,0.5,0.7,0.9]

Confiando en la inferencia de tipos de Haskell, podemos descartar el ::[Int] ya que de Integral hace que nuestra expresión lambda tenga el tipo correcto:

ghci> :t (/x -> (fromIntegral x) / 10) (/x -> (fromIntegral x) / 10) :: (Fractional a, Integral a1) => a1 -> a

Sé que las carrozas pueden conducir a comportamientos extraños en los rangos debido a su naturaleza imprecisa. Esperaría la posibilidad de valores imprecisos. Por ejemplo: [0.1,0.3..1] podría dar [0.1,0.3,0.5,0.7,0.8999999999999999] lugar de [0.1,0.3,0.5,0.7,0.9]

Además de la pérdida de precisión, sin embargo, obtengo un elemento adicional:

ghci> [0.1,0.3..1] [0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]

Esto es raro, pero se explica aquí . Podría solucionarlo así, supongo:

ghci> [0.1,0.3..0.99] [0.1,0.3,0.5,0.7,0.8999999999999999]

Pero eso es un poco asqueroso. Tal vez hay una manera más limpia. Para este simple ejemplo, por supuesto, podría usar el rango [0.1,0.3..0.9] y todo está bien.

Pero en un ejemplo más complejo, es posible que no sepa rápidamente (o me preocupe de averiguar, si soy flojo) el límite superior exacto que debería usar. Entonces, haré un rango de enteros y luego dividiré por 10, ¿verdad? Nop:

ghci> map (/10) [1,3..10] [0.1,0.3,0.5,0.7,0.9,1.1]

Cualquier función de coma flotante parece causar este comportamiento:

ghci> map (*1.0) [1,3..10] [1.0,3.0,5.0,7.0,9.0,11.0]

Mientras que una función no flotante no:

ghci> map (*1) [1,3..10] [1,3,5,7,9]

Si bien parece poco probable, pensé que tal vez estaba en juego alguna evaluación perezosa, y primero intenté forzar la evaluación del rango:

ghci> let list = [1,3..10] in seq list (map (*1.0) list) [1.0,3.0,5.0,7.0,9.0,11.0]

Obviamente, usar la lista literal en lugar del rango funciona bien:

ghci> map (*1.0) [1,3,5,7,9] [1.0,3.0,5.0,7.0,9.0] ghci> let list = [1,3,5,7,9] in seq list (map (*1.0) list) [1.0,3.0,5.0,7.0,9.0]

No se trata solo de mapear:

ghci> last [1,3..10] 9 ghci> 1.0 * (last [1,3..10]) 11.0

¿De qué manera la aplicación de una función al resultado de un rango puede afectar el resultado real evaluado de ese rango?