2017-10-15 38 views
0

J'ai des doutes à faire cet exercice. J'ai la solution (qui est apparemment fausse), mais je ne pouvais pas comprendre:Programme Haskell IO qui calcule la valeur moyenne et la plus élevée

Ecrivez un programme qui lit les entiers du périphérique d'entrée par défaut, un par ligne, à un univers négatif ou zéro, et imprime une moyenne et le plus grand des valeurs lues.

Mon code:

a6 ::Int -> Float ->Int->Int-> IO() 
a6 cBigger average2 sum2 cCount = do 
c <- getLine 
let digit = read c :: Int 
let sum = sum2 + digit 
let average = fromIntegral sum2/ fromIntegral cCount 
if (digit <=0) 
    then putStrLn("Bigger :" ++show(cBigger)++ "average "++show(cAverage)) 
    else 
    if digit > cBigger 
      then a6 digit average sum (cCount+1) 
      else a6 cBigger average sum (cCount+1) 

Comme je ne comprends pas beaucoup Haskell je doute de la façon de le faire.

Répondre

2

Bien qu'un peu hors sujet, je pensais que je voudrais commenter sur la séparation des préoccupations et la modularité.

Habituellement, nous essayons de séparer les parties pures du programme des pièces impures (IO).

Nous pouvons lire une liste de Int s avec un code impur, puis le traiter avec une fonction pure pour trouver le maximum, la somme et la longueur afin de calculer la moyenne.

Ci-dessous, readInts lit Int s de stdin jusqu'à ce qu'il lit une valeur non positive, le retour des Int positifs s dans une liste (en IO). maxSumLength prend le maximum actuel, la somme et la longueur des éléments traités jusqu'à présent en tant que tuple, et l'élément suivant à traiter, et retourne un nouveau tuple avec l'élément suivant replié. Enfin, main lit la liste de Int s, et applique un pli gauche strict (foldl') en utilisant maxSumLength et un état initial de (0, 0, 0) pour calculer le maximum final, la somme et la longueur. Il imprime ensuite le maximum et la moyenne à partir de la somme et de la longueur.

module Main where 

import Data.List (foldl') 

readInts :: IO [Int] 
readInts = do 
    i <- read <$> getLine 
    if i <= 0 
    then return [] 
    else (i:) <$> readInts 

maxSumLength :: (Int, Int, Int) -> Int -> (Int, Int, Int) 
maxSumLength (m, s, l) x = (max m x, s+x, l+1) 

main :: IO() 
main = do 
    (m, s, l) <- foldl' maxSumLength (0, 0, 0) <$> readInts 
    putStrLn $ "max=" ++ show m ++ ", avg=" ++ show (fromIntegral s/fromIntegral l) 

Ce code est plus modulaire qu'auparavant. Nous pourrions réutiliser readInts dans d'autres programmes qui ont besoin d'une liste de Int s. En outre, la partie pure de l'algorithme ne se soucie plus d'où vient la liste des Int s. Cependant, il y a un problème avec ce code.Lorsqu'il est écrit de cette façon, la liste entière doit être mise en mémoire tampon avant que le code pur puisse commencer à le traiter, même si le code de traitement peut consommer l'entrée lorsqu'elle arrive.

C'est ici que le paquet conduit peut vous aider. Le paquet conduit permet à un flux d'être produit par un Source impur et connecté à un Consumer pur, et permet au code pur d'être entrelacé avec le code impur. Le paquet conduit-combinators fournit des combinateurs qui permettent aux flux d'être traités comme des listes (en particulier, foldlC nous permet d'effectuer un pli gauche strict sur un flux de conduits au lieu d'une liste).

Dans le code ci-dessous, la fonction readInts est maintenant un Source de Int s qui fonctionne dans le IO monade. Il utilise le combinateur repeatWhileMC pour effectuer le test de bouclage et de terminaison. Le maxSumLength pur est inchangé; cependant, dans main, plutôt que d'utiliser foldl', nous utilisons foldlC pour replier le courant de conduit.

module Main where 

import Conduit 

readInts :: Source IO Int 
readInts = repeatWhileMC (read <$> getLine) (> 0) 

maxSumLength :: (Int, Int, Int) -> Int -> (Int, Int, Int) 
maxSumLength (m, s, l) x = (max m x, s+x, l+1) 

main :: IO() 
main = do 
    (m, s, n) <- runConduit (readInts =$= foldlC maxSumLength (0, 0, 0)) 
    putStrLn $ "max=" ++ show m ++ ", avg=" ++ show (fromIntegral s/fromIntegral n) 

Ce code entrelacer pur maxSumLength avec l'impur readInts de telle sorte que les Int s sont consommés comme ils sont créés, mais sans pour autant sacrifier la modularité. Le flux readInts peut être utilisé dans d'autres programmes nécessitant un flux de Int s, et le code pur ne tient plus compte de l'origine des Int s.

1

Bien qu'il ne soit pas optimal, votre programme fonctionne presque. Voici quelques corrections et nettoyages mineurs. J'ai essayé de garder votre code d'origine quand c'était possible, même s'il y avait de meilleures solutions.

D'abord, vous utilisez cAverage qui n'est pas défini. Cette erreur peut être facilement corrigée.

Le paramètre average2 est inutile, car il est inutilisé - supprimons-le.

Certains let peuvent être déplacés vers les branches où nous utilisons réellement ces variables.

Nous pouvons également effectuer un refactoring mineur et calculer la nouvelle valeur bigger en utilisant un conditionnel, au lieu d'utiliser un conditionnel pour effectuer deux appels récursifs différents. (Il serait cependant préférable d'utiliser la fonction max.)

Envisager de renommer "plus grand" en "plus grand", "plus grand" ou "maximum". Cela me semble mieux.

a6 :: Int -> Int -> Int -> IO() 
a6 bigger oldSum count = do 
c <- getLine 
let digit = read c :: Int 
if digit <= 0 
    then let average = fromIntegral oldSum/fromIntegral count :: Double 
      in putStrLn ("Bigger: " ++ show bigger ++ ", average: " ++ show average) 
    else let newBigger = if digit > bigger then digit else bigger 
       newSum = oldSum + digit 
      in a6 newBigger newSum (count+1)