2017-08-20 5 views
3

Est-ce que getLine est paresseux?Est-ce que getLine est paresseux?

Dites que j'ai une très longue ligne sur l'entrée. C'est juste une séquence de nombres. Je n'ai besoin que de sommer 3 premiers nombres. Est-ce que sera efficace et ne lira que la première partie de la ligne, ou dois-je créer ma propre fonction pour la lecture de ligne paresseuse, qui lirait les caractères un par un?

Mon implémentation sera-t-elle efficace si je devais additionner toute la ligne? (Ce qu'il y aura une surcharge en raison de lire les caractères un par un?)

import Control.Applicative 

main = do 
    line <- getLine' 
    print $ sum $ map read $ take 3 $ words line 

getLine' :: IO String 
getLine' = do 
    c <- getChar 
    if c == '\n' then return [] else (c:) <$> getLine' 
+0

Je pense que la bibliothèque 'getLine' et votre' getLine'' sont toutes les deux strictement strictes. Les actions d'E/S ne peuvent pas retourner paresseusement à moins d'utiliser une fonction 'dangereuse '- c'est ce que l'on appelle les' IO paresseux 'et doivent être manipulées avec précaution, puisque la lecture commencera plus tard à cause de la paresse. Lazy IO est (in) célèbre pour le débogage. Vous pouvez cependant utiliser une coutume stricte 'get3Ints' qui ne lit que la partie de la chaîne dont vous avez besoin. – chi

+3

'getLine' doit être strict pour être correct, et comme chi dit que' getLine'' se comporte exactement de la même façon. Si 'getLine' était non-strict, alors les calculs purs que vous ferez plus tard causeraient des E/S, en réalisant plus de caractères à partir de l'entrée paresseuse. Ce serait un cauchemar quand vous considérez que d'autres E/S peuvent aussi se passer, en lisant aussi depuis stdin: quels personnages vont où sera extrêmement difficile à comprendre. – amalloy

+1

Cf. [cette réponse] (https://codereview.stackexchange.com/a/120037/16551) pour en savoir un peu plus sur «IO» et la paresse. Si vous voulez un 'getLine' paresseux, il aura probablement besoin d'un type similaire à' IO (ListT IO Char) 'où' dataT m a = Nil | Cons a (m (ListT m a)) '(Vous pouvez aussi avoir' ListT IO String' au lieu de 'ListT IO Char' si vous lisez l'entrée en morceaux d'une longueur donnée pour être plus efficace). – gallais

Répondre

1

Alors que getLine est pas paresseux, getContents est, et il peut être combiné avec des fonctions comme lines et words. Par conséquent, le programme suivant ne lit assez stdin pour obtenir (jusqu'à) trois entiers de la première ligne et imprimer leur somme:

main :: IO() 
main = do contents <- getContents 
      let lns = lines contents 
       result = sum $ map read $ take 3 $ words $ head lns 
      print (result :: Integer) 

Notez que, si vous modifiez le programme pour accéder aux lignes suivantes - pour par exemple, si vous avez ajouté:

putStrLn $ take 80 $ lns !! 1 

au fond du programme pour imprimer les 80 premiers caractères de la deuxième ligne, le programme devrait terminer la lecture de la première ligne (et serait donc accrocher un peu entre les deux dernières lignes du programme) avant de traiter les 80 premiers caractères de la seconde. En d'autres termes, cette lecture de ligne paresseuse n'est utile que si vous avez besoin de lire le premier bit de la première ligne, si cela n'était pas évident pour vous - Haskell n'a aucun moyen magique de passer le reste de la première ligne pour arriver à la seconde. Enfin, notez que, pour le programme ci-dessus, s'il y a moins de trois entiers sur la première ligne, cela ne fera que sommer ces nombres et n'essaiera pas de lire après la première ligne (ce que je pense est ce que tu voulais). Si vous ne vous souciez pas vraiment des fins de ligne et que vous souhaitez simplement additionner les trois premiers chiffres du fichier, peu importe comment ils sont divisés en lignes, vous pouvez en diviser le contenu directement en mots comme suit:

main = do contents <- getContents 
      let result = sum $ map read $ take 3 $ words contents 
      print (result :: Integer)