Чтение файла Haskell
Недавно я начал изучать 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 Я прочитал его полностью
Ответы
Ответ 1
Неплохое начало! Единственное, что нужно помнить, это то, что приложение чистой функции должно использовать 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
Это минимальное изменение, необходимое для компиляции и запуска этой вещи. Стилистически, у меня есть несколько комментариев:
- Связывание
list
дважды выглядит немного теневым. Обратите внимание, что это не мутирует значение list
- вместо этого затеняет старое определение.
- Встроенные чистые функции намного больше!
- Когда возможно, использование
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
Ответ 2
Решение Daniel Wagner - отличное решение. Вот еще один поворот в нем, чтобы вы могли получить больше идей об эффективной обработке файлов.
{-# 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 для обработки файла по нескольким причинам. Во-первых, это позволяет нам передавать файл через программу, а не сразу заставлять все это в памяти. Кроме того, bytestrings быстрее и эффективнее, чем строки в целом.
Все остальное довольно просто. Мы читаем файл в ленивый список строк, а затем сопоставляем функцию суммирования по каждой из строк. <$>
- это просто ярлыки, позволяющие нам управлять значением внутри функтора IO() - если это слишком много, я приношу свои извинения. Я просто имею в виду, что когда вы читаетеFile, вы не возвращаете ByteString, вы возвращаете ByteString, завернутый в IO IO (ByteString). <$>
говорит: "Эй, я хочу работать с вещью внутри IO, а затем завернуть ее обратно.
B.split разделяет каждую строку на числа, основанные на пробеле. (Мы могли бы также использовать B.words для этого). Еще одна интересная часть - это в sumStrs
мы используем сопоставление деконструкции/шаблона для извлечения первого значения из Just, которое возвращается функцией readInt.
Надеюсь, это было полезно. Спросите, есть ли у вас какие-либо вопросы.
Ответ 3
Для всех вас неработающие программисты здесь - это удовольствие
unsafePerformIO . readFile $ "file.txt"
Считывает файл в строку
No IO String, просто нормальная полностью загруженная строка готова для использования.
Это может быть неправильным, но это работает, и нет необходимости изменять существующие функции в соответствии с IO String
p.s.
Не забывайте импортировать
import System.IO.Unsafe