web-dev-qa-db-fra.com

Lecture de fichier Haskell

Je viens tout juste de commencer à apprendre Haskell et j'ai beaucoup de difficulté à comprendre comment fonctionne la lecture de fichier.

Par exemple, j'ai un fichier texte "test.txt" Et il contient des lignes de chiffres, par exemple:

32 4
2 30
300 5

Je veux lire chaque ligne, puis évaluer chaque mot et les ajouter .. C'est pourquoi j'essaie de faire quelque chose comme ça jusqu'à présent:

import System.IO  
import Control.Monad

main = do  
        let list = []
        handle <- openFile "test.txt" ReadMode
        contents <- hGetContents handle
        singlewords <- (words contents)
        list <- f singlewords
        print list
        hClose handle   

f :: [String] -> [Int]
f = map read

Je sais que cela est complètement faux, mais je ne sais pas comment utiliser la syntaxe correctement du tout… .. Toute aide sera grandement appréciée. Ainsi que des liens vers de bons tutoriels qui ont des exemples et une explication de code sauf celui-ci: http://learnyouahaskell.com/input-and-output je l'ai lu intégralement

25
DustBunny

Pas mal comme départ! La seule chose à retenir est que l'application de fonction pure devrait utiliser let au lieu de la liaison <-.

import System.IO  
import Control.Monad

main = do  
        let list = []
        handle <- openFile "test.txt" ReadMode
        contents <- hGetContents handle
        let singlewords = words contents
            list = f singlewords
        print list
        hClose handle   

f :: [String] -> [Int]
f = map read

C’est le changement minimal nécessaire pour que la chose soit compilée et exécutée. Stylistiquement, j'ai quelques commentaires:

  1. La liaison list a l'air un peu louche deux fois. Notez que cela ne modifie pas la valeur list, mais remplace l'ancienne définition.
  2. Inline pure fonctionne beaucoup plus!
  3. Si possible, utilisez readFile est préférable à l’ouverture, la lecture et la fermeture manuelle d’un fichier.

L'implémentation de ces changements donne quelque chose comme ceci:

main = do  
        contents <- readFile "test.txt"
        print . map readInt . words $ contents
-- alternately, main = print . map readInt . words =<< readFile "test.txt"

readInt :: String -> Int
readInt = read
49
Daniel Wagner

La solution de Daniel Wagner est excellente. Voici un autre coup d'oeil pour que vous puissiez avoir plus d'idées sur la gestion efficace des fichiers.

{-#  LANGUAGE OverloadedStrings #-}
import System.IO
import qualified Data.ByteString.Lazy.Char8 as B
import Control.Applicative
import Data.List

sumNums :: B.ByteString -> Int
sumNums s = foldl' sumStrs 0 $ B.split ' ' s

sumStrs :: Int -> B.ByteString -> Int
sumStrs m i = m+int
              where Just(int,_) = B.readInt i

main = do 
  sums <- map sumNums <$> B.lines <$> B.readFile "testy"
  print sums

Tout d'abord, vous verrez le pragma OverloadedStrings. Cela permet d'utiliser simplement des guillemets normaux pour les littéraux de chaîne qui sont en réalité des chaînes de caractères. Nous allons utiliser Lazy ByteStrings pour le traitement du fichier pour plusieurs raisons. Premièrement, cela nous permet de diffuser le fichier dans le programme plutôt que de tout mettre en mémoire en une fois. De plus, les bytestrings sont plus rapides et plus efficaces que les chaînes en général. 

Tout le reste est à peu près simple. Nous lisonsFichier le fichier dans une liste paresseuse de lignes, puis mappons une fonction de sommation sur chacune des lignes. Les <$> ne sont que des raccourcis nous permettant d’opérer sur la valeur à l’intérieur du foncteur IO () - si c’est trop, je m'en excuse. Je veux simplement dire que lorsque vous lisez File, vous ne récupérez pas une chaîne Byte, vous récupérez une chaîne Byte encapsulée dans IO sous la forme d'un IO (ByteString). Le <$> indique "Hé" je veux utiliser la chose à l'intérieur du IO, puis l'envelopper.

B.split sépare chaque ligne en nombres basés sur des espaces. (Nous pourrions également utiliser B.words pour cela). La seule autre partie intéressante est le in sumStrs, nous utilisons deconstruction/pattern matching pour extraire la première valeur de Just qui est renvoyée par la fonction readInt.

J'espère que cela a été utile. Demandez si vous avez des questions.

7
Erik Hinton

Pour tous les programmeurs non fonctionnels, voici un régal

unsafePerformIO . readFile $ "file.txt"

Lit un fichier dans une chaîne

No IO String, juste une chaîne normale complètement chargée prête à être utilisée . Ce n'est peut-être pas la bonne façon, mais cela fonctionne et il n'est pas nécessaire de modifier les fonctions existantes pour qu'elles correspondent à IO

p.s . N'oubliez pas d'importer

import System.IO.Unsafe 
0
Kapytanhook