Haskell dividiendo num
Pregunta
Tengo un problema sobre el que quiero obtener más información y cómo evitarlo. Tengo este código
len :: (Num r ) => [a] -> r
len [] = 0
len xs = 1 + len ( tail xs )
avg :: (Num t) => [t] -> Double
avg xs = ( sum xs ) / ( len xs )
Lo que genera el siguiente error
len.hs:6:9: Couldn't match expected type `Double' against inferred type `t' `t' is a rigid type variable bound by the type signature for `avg' at len.hs:5:12 In the expression: (sum xs) / (len xs) In the definition of `avg': avg xs = (sum xs) / (len xs)
Ahora, sé que este error (gracias a irc.freenode.net # haskell) es el resultado de la función de división
(/) :: (Fractional a) => a -> a -> a
Sin embargo, no sé qué hacer. Mi firma de función avg
no debería tener nada que ver con las peculiaridades de los operadores de división (que requieren la clase de tipo Fractional
). Entonces, me quedo pensando que la forma correcta de superar esto es emitiendo un tipo que implique que Fractional
typeclass pero no tengo idea de cómo, o incluso si esto es correcto. ¿Alguna idea?
Solución
Mi firma de función
avg
no debería tener nada que ver con las peculiaridades del operador de división
¿Por qué es eso? Si desea calcular el promedio de un conjunto de números enteros, tendrá que dividir en algún momento, por lo que tendrá que convertirlos de números enteros al tipo de soporte de división de su elección. Una mirada cercana a la clase Num
(: i Num
en ghci) revela un problema con el tipo de avg
: Num
no tiene suficientes métodos, básicamente suficientes para sumar, multiplicar y restar. No hay garantía de que el número que le doy a avg
se pueda convertir a Double
en absoluto.
Si ingresa una función sin tipo para calcular un promedio, Haskell responde con el tipo más genérico posible:
Prelude List> :type \x -> sum x / genericLength x
\x -> sum x / genericLength x :: (Fractional a) => [a] -> a
Entonces, ese es el tipo correcto de avg
.
Puede notar que avg [1,2,3 :: Integer]
da un error de tipo. Puede evitar eso pasando primero el argumento a toRational
o fromIntegral
, que utilizan las instancias Real
y Integral
para Integer
, respectivamente.
Con respecto a la expresión sum [1,2,3] / len [1,2,3]
: Es cierto que un número literal como 1
tiene el tipo de Num a = > a
, que llama a fromInteger
en cualquier tipo que resulte ser, pero una expresión como 1/2
tiene un tipo más específico de Fraccional a = > un
, que puede ver si solicita el tipo de esa expresión en lugar de imprimirla.
Algo que podría ser útil es : set -Wall
en ghci, que activa muchas advertencias cada vez que se elige un tipo predeterminado para usted, lo que da una pista de que el tipo más genérico ya no será correcto.
Otros consejos
Estás restringiendo demasiado el tipo de promedio. Use la versión más general, avg :: (Fractional a) = > [a] - > a
hhm, el verdadero problema es si es un tipo integral que desea convertir a un tipo fraccionario, pero si es un tipo fraccional, desea dejarlo solo.
Prueba esto
fromRational ((sum xs) % (leng xs))
He encontrado este problema. Lo mejor que he logrado hacer es tener dos funciones para promediar: una para integrales y otra para fraccionales:
avgInt :: (Integral i, Fractional f) => [i] -> f
avgInt xs = fromIntegral (sum xs) / fromIntegral (length xs)
avgFrac :: (Fractional f) => [f] -> f
avgFrac xs = sum xs / fromIntegral (length xs)