Could not deduce (a ~ Int) bei einfacher Funktion
-
Hallo,
ich lerne gerade etwas Haskell, ich bin schwer durch imperative Programmierung geschädigt und tu mich nun relativ schwer.
Eine Funktion soll eine Liste von Ganzzahlen entgegennehmen, einen Notenschnitt daraus berechnen und als Ausgabe einen Kommentar dazu abgeben.
Ich habe das so probiert:
grades :: (Num a) => [a] -> String grades m | gs < 1 = "Excellent" | gs < 2 = "Very good" | gs < 3 = "Good" | gs < 4 = "Okay... What are we going to eat tonight?" | otherwise = "Bad" where gs = (sum m) / (length m)
Aufruf:
print(grades [2,2,2,1,1])
Leider erhalte ich lediglich folgende Meldung:
Could not deduce (a ~ Int) from the context (Num a) bound by the type signature for grades :: Num a => [a] -> String at helloworld.hs:(36,1)-(42,39) `a' is a rigid type variable bound by the type signature for grades :: Num a => [a] -> String at helloworld.hs:36:1 In the return type of a call of `length' In the second argument of `(/)', namely `(length m)' In the expression: (sum m) / (length m)
Offenbar passt ihm etwas in der Typinferenz nicht... allerdings verstehe / sehe ich nicht genau woran es liegt...
Wer kann mir helfen?
-
-- am besten guckst du dir die Typen an Prelude> :t (/) (/) :: Fractional a => a -> a -> a Prelude> :t div div :: Integral a => a -> a -> a Prelude> :t length length :: [a] -> Int Prelude> :t sum sum :: Num a => [a] -> a -- wenn die Typen zusammen passen, dann funktioniert es auch Prelude> let xs = [1..5] :: [Int] Prelude> :t xs xs :: [Int] Prelude> :t length xs length xs :: Int Prelude> :t sum xs sum xs :: Int Prelude> sum xs `div` length xs 3 -- nochmal Prelude> let xs = [1..5] Prelude> :t xs xs :: [Integer] -- Integer statt Int Prelude> :t length xs length xs :: Int -- Int Prelude> :t sum xs sum xs :: Integer -- Integer Prelude> sum xs `div` length xs -- Int `div` Integer -> aua <interactive>:87:14: Couldn't match expected type `Integer' with actual type `Int' In the return type of a call of `length' In the second argument of `div', namely `length xs' In the expression: sum xs `div` length xs Prelude> sum xs `div` (fromIntegral $ length xs) 3 -- und jetzt für Fractional Prelude> sum xs / 1.0 <interactive>:101:8: No instance for (Fractional Integer) arising from a use of `/' Possible fix: add an instance declaration for (Fractional Integer) In the expression: sum xs / 1.0 In an equation for `it': it = sum xs / 1.0 Prelude> (sum xs) / 1.0 <interactive>:102:10: No instance for (Fractional Integer) arising from a use of `/' Possible fix: add an instance declaration for (Fractional Integer) In the expression: (sum xs) / 1.0 In an equation for `it': it = (sum xs) / 1.0 Prelude> (fromIntegral $ sum xs) / 1.0 15.0 Prelude> (fromIntegral $ sum xs) / (length xs) <interactive>:104:25: No instance for (Fractional Int) arising from a use of `/' Possible fix: add an instance declaration for (Fractional Int) In the expression: (fromIntegral $ sum xs) / (length xs) In an equation for `it': it = (fromIntegral $ sum xs) / (length xs) Prelude> (fromIntegral $ sum xs) / (fromIntegral $ length xs) 3.0
Führt zu:
grades :: (Integral a) => [a] -> String grades m | gs < 1 = "Excellent" | gs < 2 = "Very good" | gs < 3 = "Good" | gs < 4 = "Okay... What are we going to eat tonight?" | otherwise = "Bad" where gs = (fromIntegral $ sum m) / (fromIntegral $ length m)
-
Oder auch mit
genericLength
ausData.List
:grades :: Integral a => [a] -> String grades = (text !!) . fromIntegral . min 0 . mean where mean xs = (sum xs) `div` (genericLength xs) text = [ "Excellent" , "Very good" , "Good" , "Okay... What are we going to eat tonight?" ] ++ (repeat "Bad")
-
Vielen Dank für die Antworten, Frage geklärt
-
Vielleicht noch ein kleiner Nachtrag:
Es ist eine gute Strategie, die Signatur erstmal wegzulassen, und gucken, was dabei herumkommt.
Die length-Funktion ist seltsamerweise als (output) -> Int definiert, was bei diesen und ähnlichen Aufgaben vermutlich öfter als ärgerliches Hindernis bei Anfängern auftauchen wird. Rein aus technischer Sicht ist irgendwie auch nicht ganz klar, was daran so schwer scheint, eine "Int" Zahl in die FPU zu schmeissen.Wie auch immer, wenn man die length-Funktion (notgedrungen) selbst schreibt, und dabei den Out in eine höhere (oder speziellere) Klasse hebt (z.B. Num a), also z.B. mit
mylength :: Num a => [a] -> a mylength [] = 0 usw.
Dann verschwindet das obige Problem erstmal.
Man kann sich dann mit :info grades die dazu passende Signatur angucken, und die neue Funktion mit
mit grades ... oder putStrLn (grades...) aufrufen.
(und sollte dann auch die Fehlermeldungen verstehen )