Haskell divisant le nombre
Question
J'ai un problème sur lequel je veux en savoir plus et comment éviter. J'ai ce code
len :: (Num r ) => [a] -> r
len [] = 0
len xs = 1 + len ( tail xs )
avg :: (Num t) => [t] -> Double
avg xs = ( sum xs ) / ( len xs )
Qui rend l'erreur suivante
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)
Maintenant, je sais que cette erreur (grâce à irc.freenode.net # haskell) est le résultat de la fonction de division
(/) :: (Fractional a) => a -> a -> a
Cependant, je ne sais pas quoi faire. Ma signature de fonction avg
ne devrait avoir aucun rapport avec les excentivants d'opérateurs de division (nécessitant la classe de type Fractional
). Donc, je suis parti pour penser que la bonne façon de surmonter ceci est de choisir un type qui implicite leur classe Fractional
mais je ne sais pas comment, ni même si c'est correct? Des idées?
La solution
Lasignature de ma fonction
avg
ne doit avoir aucun rapport avec les défauts de l'opérateur de division
Pourquoi ça? Si vous souhaitez calculer la moyenne d'un groupe d'entiers, vous devrez diviser à un moment donné. Vous devrez donc les convertir d'Integers au type prenant en charge la division de votre choix. Un examen attentif de la classe Num
(: i Num
en ghci) révèle un problème avec le type de avg
: Num
n'a pas assez de méthodes, c'est-à-dire assez pour additionner, multiplier et soustraire. Rien ne garantit que le numéro que je donne à avg
puisse être converti en Double
.
Si vous entrez une fonction non typée pour calculer une moyenne, Haskell répond avec le type le plus générique possible:
Prelude List> :type \x -> sum x / genericLength x
\x -> sum x / genericLength x :: (Fractional a) => [a] -> a
C'est donc le bon type de avg
.
Vous remarquerez peut-être que avg [1,2,3 :: Integer]
génère une erreur de type. Vous pouvez contourner cela en passant l'argument à toRational
ou fromIntegral
, qui utilise les instances Real
et Integral
. pour entier
, respectivement.
À propos de l'expression sum [1,2,3] / len [1,2,3]
: Il est vrai qu'un nombre littéral tel que 1
a le type de Num a = > a
, qui appelle fromInteger
quel que soit le type utilisé, mais une expression comme 1/2
a un type plus spécifique de Fractional a = > un
, que vous pouvez voir si vous demandez le type de cette expression au lieu de l’imprimer.
Un élément qui pourrait être utile est : set -Wall
dans ghci, qui active de nombreux avertissements chaque fois qu'un type par défaut est choisi, ce qui indique que le type le plus générique pourrait ne plus l'être. correct.
Autres conseils
Vous êtes trop contraignant pour le type d'avg. Utilisez la version plus générale, avg :: (Fractional a) = > [a] - > un
hhm, le vrai problème est de savoir si c'est un type entier que vous voulez convertir en un type fractionnel, mais s'il s'agit d'un type fractionnel, vous voulez le laisser seul.
Essayez ceci
fromRational ((sum xs) % (leng xs))
J'ai rencontré ce problème. Ce que j’ai le mieux réussi à faire est d’avoir deux fonctions pour la moyenne: une pour les intégrales et une pour les fractions:
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)