Question

Je suis jouer avec débutant Haskell, et je voulais écrire une fonction moyenne. Il semblait que la chose la plus simple du monde, non?

Mauvais.

Il semble que le système de type Haskell interdit de travailler en moyenne sur un type numérique générique - je peux l'obtenir pour travailler sur une liste des Intégrales, ou une liste de Fractionals, mais pas les deux

.

Je veux:

average :: (Num a, Fractional b) => [a] -> b
average xs = ...

Mais je peux seulement obtenir:

averageInt :: (Integral a, Fractional b) => [a] -> b
averageInt xs = fromIntegral (sum xs) / fromIntegral (length xs)

ou

averageFrac :: (Fractional a) => [a] -> a
averageFrac xs = sum xs / fromIntegral (length xs)

et le second semble fonctionner. Jusqu'à ce que je tente de passer une variable.

*Main> averageFrac [1,2,3]
2.0
*Main> let x = [1,2,3]
*Main> :t x
x :: [Integer]
*Main> averageFrac x

<interactive>:1:0:
    No instance for (Fractional Integer)
      arising from a use of `averageFrac ' at <interactive>:1:0-8
    Possible fix: add an instance declaration for (Fractional Integer)
    In the expression: average x
    In the definition of `it': it = averageFrac x

Apparemment, Haskell est vraiment pointilleux sur ses types. Ça a du sens. Mais pas quand ils pourraient tous deux être [Num]

Suis-je manque une application évidente de RealFrac?

Y at-il moyen de forcer Intégrales en Fractionals qui ne s'étouffe pas quand il fait une entrée Fractional?

Est-il possible d'utiliser Either et either pour faire une sorte de fonction moyenne polymorphes qui fonctionnerait sur toute sorte de tableau numérique?

Est-ce que le système de type Haskell interdit purement et simplement cette fonction de jamais existante?

L'apprentissage Haskell est comme l'apprentissage Calcul. Il est vraiment complexe et basé sur les montagnes de la théorie, et parfois le problème est si complexe mindbogglingly que je ne sais même pas assez pour la phrase correctement à la question, de sorte que toute idée sera chaleureusement acceptée.

(En outre, la note:. C'est basé sur un problème de devoirs Tout le monde convient que averageFrac, au-dessus, obtient des points pleins, mais je soupçonne de Furtif qu'il ya un moyen de le faire fonctionner sur les deux tableaux intégrale et fractionnaires)

Était-ce utile?

La solution

Alors fondamentalement, vous êtes limité par le type de (/):

(/) :: (Fractional a) => a -> a -> a

BTW, vous voulez aussi Data.List.genericLength

genericLength :: (Num i) => [b] -> i

Alors que diriez-vous de retirer la fromIntegral quelque chose de plus général:

import Data.List

average xs = realToFrac (sum xs) / genericLength xs

qui a seulement une contrainte réelle (Int, Integer, Float, Double) ...

average :: (Real a, Fractional b) => [a] -> b

Alors que va prendre tout réel dans toute fractionnaires.

Et notez toutes les affiches de se faire prendre par les littéraux numériques polymorphes dans Haskell. 1 ne soit pas un nombre entier, il est un nombre quelconque.

La vraie classe ne fournit qu'une seule méthode: la capacité de transformer une valeur en classe Num à un rationnel. Ce qui est exactement ce dont nous avons besoin ici.

Et donc,

Prelude> average ([1 .. 10] :: [Double])
5.5
Prelude> average ([1 .. 10] :: [Int])
5.5
Prelude> average ([1 .. 10] :: [Float])
5.5
Prelude> average ([1 .. 10] :: [Data.Word.Word8])
5.5

Autres conseils

La question a été très bien répondu à de DONS, je pensais que je pourrais ajouter quelque chose.

Lors du calcul de la moyenne de cette façon:

average xs = realToFrac (sum xs) / genericLength xs

Qu'est-ce que votre code va faire est de parcourir la liste deux fois, une fois pour calculer la somme de ses éléments, et une fois pour obtenir sa longueur. Pour autant que je sache, GHC n'est pas encore en mesure d'optimiser cela et calculer à la fois la somme et la longueur en une seule passe.

Il ne fait pas mal, même en tant que débutant à penser et sur les solutions possibles, par exemple la fonction moyenne peut être écrit en utilisant un pli qui calcule à la fois la somme et la durée; sur ghci:

:set -XBangPatterns

import Data.List

let avg l=let (t,n) = foldl' (\(!b,!c) a -> (a+b,c+1)) (0,0) l in realToFrac(t)/realToFrac(n)

avg ([1,2,3,4]::[Int])
2.5
avg ([1,2,3,4]::[Double])
2.5

La fonction ne semble pas aussi élégant, mais la performance est meilleure.

Plus d'informations sur le blog Dons:

http://donsbot.wordpress.com/2008/06/04/haskell-as-fast-as-c-working-at-a-high-altitude-for-low-level -Performance /

Depuis enfile a fait un bon travail à répondre à votre question, je vais travailler sur l'interrogatoire votre question ....

Par exemple, dans votre question, vous exécutez d'abord une moyenne sur une liste donnée, obtenir une bonne réponse. Ensuite, vous prenez ce qui ressemble à la même liste exacte , l'assigner à une variable, puis utilisez la fonction de la variable ... qui souffle ensuite.

Qu'est-ce que vous avez rencontré ici est un set-up dans le compilateur, appelé le DMR: D readed M onomorphic R estriction. Lorsque vous avez passé la liste directement dans la fonction, le compilateur n'a fait aucune hypothèse sur quel type étaient les chiffres, juste inférée quels types il pourrait être basé sur l'utilisation, puis une fois cueillies, il ne pouvait pas réduire le champ plus. Il est un peu comme le contraire de canard frappe, là.

Quoi qu'il en soit, lors de l'attribution de la liste à une variable, le DMR a débuté dans. Puisque vous avez mis la liste dans une variable, mais étant donné aucune indication sur la façon dont vous voulez l'utiliser, le DMR a fait le compilateur choisir un type , dans ce cas, il a choisi un qui correspondait à la forme et semblait: Integer. Étant donné que votre fonction ne pouvait pas utiliser un nombre entier dans son fonctionnement de / (il a besoin d'un type dans la classe Fractional), il fait cette plainte même: il n'y a aucune instance de Integer dans la classe Fractional. Il y a des options que vous pouvez définir dans GHC afin qu'il ne force pas vos valeurs dans une forme unique ( « mono-morphique », l'obtenir?) Jusqu'à ce qu'il a besoin, mais il fait des messages d'erreur un peu plus difficile à comprendre.

Maintenant, sur une autre note, vous aviez une réponse à la réponse de dons qui a attiré mon attention:

  

Je suis trompé par le tableau de la dernière page de cs.ut.ee/~varmo/MFP2004/PreludeTour.pdf   qui montre Floating pas héritant des propriétés de Real, et je suppose alors que   ils partageraient aucun type en commun.

Haskell ne types différemment de ce que vous êtes habitué. Real et Floating sont des classes de type, qui fonctionnent plus comme interfaces que les classes d'objets. Ils vous disent ce que vous pouvez faire avec un type qui est dans cette classe, mais cela ne signifie pas que certains type ne peut pas faire d'autres choses, plus que d'avoir un moyen d'interface qu'une classe (n-style OO) ne peut pas ont les autres.

  

L'apprentissage Haskell est comme l'apprentissage Calcul

Je dirais que d'apprendre Haskell est comme apprendre le suédois - il y a beaucoup de petites choses, simples (lettres, chiffres) qui ressemblent et fonctionnent de la même, mais il y a aussi des mots qui ressemblent à elles devrait signifier une chose, quand ils en fait signifier autre chose. Mais une fois que vous obtenez couramment, vos amis réguliers seront surpris par la façon dont vous pouvez débiter ce genre de choses hurluberlu qui fait beautés magnifiques faire des tours incroyables. Curieusement, il y a beaucoup de gens impliqués dans Haskell des débuts, qui connaissent aussi le suédois. Peut-être que la métaphore est plus que juste une métaphore ...

:m Data.List
let list = [1..10]
let average = div (sum list) (genericLength list)
average

Oui, le système de type de Haskell est très pointilleux. Le problème est ici le type de fromIntegral:

Prelude> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b

fromIntegral sera uniquement accepter une intégrale comme, aucun autre type de Num. (/), D'autre part accepte que fractionnelle. Comment allez-vous faire les deux travaillent ensemble?

Eh bien, la fonction somme est un bon début:

Prelude> :t sum
sum :: (Num a) => [a] -> a

Somme prend une liste de tous Num et retourne un Num.

Votre problème suivant est la longueur de la liste. La longueur est un int:

Prelude> :t length
length :: [a] -> Int

Vous devez convertir cette Int dans un Num aussi bien. C'est ce que fait fromIntegral.

vous avez donc maintenant obtenu une fonction qui renvoie une Num et une autre fonction qui renvoie un Num. Il y a des règles pour la promotion du type de numéros vous pouvez rechercher , mais au fond, à ce stade, vous bon pour aller:

Prelude> let average xs = (sum xs) / (fromIntegral (length xs))
Prelude> :t average
average :: (Fractional a) => [a] -> a

Donnons-lui un essai:

Prelude> average [1,2,3,4,5]
3.0
Prelude> average [1.2,3.4,5.6,7.8,9.0]
5.4
Prelude> average [1.2,3,4.5,6,7.8,9]
5.25
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top