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.