Lecture du fichier Haskell

J'ai récemment commencé à apprendre Haskell et j'ai beaucoup de mal à essayer de comprendre comment fonctionne la lecture du fichier.

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

32 4
2 30
300 5

Je veux lire chaque ligne, puis évaluer chaque mot et les ajouter. Ainsi, 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 c'est complètement faux, mais je ne sais pas comment utiliser la syntaxe exacte. Toute aide sera grandement apprécier. Ainsi que des liens vers de bons tutoriels qui ont des exemples et une explication du code sauf celui ci: http://learnyouahaskell.com/input-and-output, je l'ai lu entièrement

23
demandé sur DustBunny 2011-10-23 21:11:20

3 réponses

Pas un mauvais départ! La seule chose à retenir est que l'application Pure function doit 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 deux fois semble un peu ombragée. Notez que cela ne mute pas la valeur list - c'est plutôt l'ombre de l'ancienne définition.
  2. Inline fonctions pures beaucoup plus!
  3. si possible, l'utilisation de readFile est préférable à l'ouverture manuelle, la lecture et la fermeture d'un fichier.

La mise en œuvre 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
46
répondu Daniel Wagner 2011-10-23 17:21:40

La solution de Daniel Wagner est géniale. Voici une autre balançoire 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 fait des bytestrings. Nous utiliserons des ByteStrings paresseux pour traiter le fichier pour plusieurs raisons. Tout d'abord, il nous permet de diffuser le fichier à travers le programme plutôt que de tout forcer en mémoire à la fois. Également, les bytestrings sont plus rapides et plus efficaces que les chaînes en général.

Tout le reste est assez simple. Nous lisons le fichier dans une liste de lignes paresseuse, puis mappons une fonction de sommation sur chacune des lignes. Les <$> ne sont que des raccourcis pour nous permettre d'opérer sur la valeur à l'intérieur du foncteur IO () - si c'est trop, je m'excuse. Je veux juste dire que lorsque vous readFile vous ne récupérez pas un ByteString, vous récupérez un ByteString enveloppé dans IO un IO (ByteString). Le <$> dit "Hey' je veux opérer sur la chose à l'intérieur de L'E / S, puis l'envelopper.

B. split sépare chaque ligne en nombres en fonction des espaces. (Nous pourrions aussi utiliser B. words pour cela) la seule autre partie intéressante est la in sumStrs nous utilisons la déconstruction / pattern matching pour extraire la première valeur du Juste qui est renvoyée par la fonction readInt.

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

6
répondu Erik Hinton 2011-10-23 17:32:12

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

unsafePerformIO . readFile $ "file.txt"

Lit un fichier dans une chaîne

Pas de chaîne D'E / S, juste une chaîne normale entièrement chargée prête à l'emploi. Ce n'est peut-être pas la bonne façon, mais cela fonctionne et pas besoin de changer vos fonctions existantes pour convenir à Io String

P. s. N'oubliez pas d'importer

import System.IO.Unsafe 
0
répondu Kapytanhook 2013-06-23 21:01:50