Чтение файлов на Haskell - PullRequest
       14

Чтение файлов на Haskell

25 голосов
/ 23 октября 2011

Я только недавно начал изучать Haskell, и у меня много проблем с попытками выяснить, как работает чтение файла.

Например, у меня есть текстовый файл "test.txt", и он содержитстроки чисел, например:

32 4
2 30
300 5

Я хочу прочитать каждую строку, а затем оценить каждое слово и добавить их.Таким образом, я пытаюсь сделать что-то вроде этого:

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

Я знаю, что это совершенно неправильно, но я вообще не знаю, как правильно использовать синтаксис.Любая помощь будет оценена.А также ссылки на хорошие учебники, в которых есть примеры и объяснения кода, кроме этого: http://learnyouahaskell.com/input-and-output Я прочитал его полностью

Ответы [ 3 ]

50 голосов
/ 23 октября 2011

Неплохое начало! Единственное, что нужно помнить, это то, что приложение чистой функции должно использовать let вместо привязки <-.

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

Это минимальное изменение, необходимое для компиляции и запуска. Стилистически у меня есть несколько комментариев:

  1. Binding list дважды выглядит немного тенистым. Обратите внимание, что это не изменяет значение list - оно затеняет старое определение.
  2. Встроенные чистые функции намного больше!
  3. По возможности, использование readFile предпочтительнее ручного открытия, чтения и закрытия файла.

Реализация этих изменений дает что-то вроде этого:

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
8 голосов
/ 23 октября 2011

Даниэль Вагнер - отличное решение. Вот еще один пример, чтобы вы могли получить больше идей об эффективной обработке файлов.

{-#  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

Сначала вы увидите прагму OverloadedStrings. Это позволяет использовать обычные кавычки для строковых литералов, которые на самом деле являются строками байтов. Мы будем использовать Lazy ByteStrings для обработки файла по нескольким причинам. Во-первых, это позволяет нам транслировать файл через программу, а не принудительно помещать его в память сразу. Кроме того, байтовые строки быстрее и эффективнее, чем строки в целом.

Все остальное в значительной степени просто. Мы читаем файл в ленивый список строк, а затем сопоставляем функцию суммирования по каждой из строк. <$> - это просто ярлыки, позволяющие нам работать со значением внутри функтора IO () - извините, если это слишком много. Я просто имею в виду, что когда вы читаете File, вы не возвращаете ByteString, вы возвращаете ByteString, завернутый в IO и IO (ByteString). <$> говорит: «Эй, я хочу поработать с вещью внутри IO, а затем снова обернуть ее.

B.split разделяет каждую строку на числа на основе пробела. (Мы могли бы также использовать B.words для этого) Единственная другая интересная часть - это в sumStrs, который мы используем для деконструкции / сопоставления с образцом, чтобы извлечь первое значение из Just, которое возвращается функцией readInt.

Надеюсь, это было полезно. Спросите, если у вас есть какие-либо вопросы.

0 голосов
/ 24 июня 2013

Для всех вас, неработающих программистов, здесь есть угощение

unsafePerformIO . readFile $ "file.txt"

Считывает файл в строку

Нет строки ввода-вывода, просто обычная полностью загруженная строка, готовая к использованию,Возможно, это не правильный путь, но он работает, и вам не нужно менять существующие функции в соответствии с IO String

ps. Не забудьте импортировать

import System.IO.Unsafe 
...