Haskell: вставка каждой строки из файла в список

В настоящее время я работаю над проектом с Haskell, и у меня возникли проблемы. Я должен читать и вставлять в список каждую строку в файле "dictionary.txt", но я не могу этого сделать. У меня есть такой код:

main = do
    let list = []
    loadNums "dictionary.txt" list

loadNums location list = do
    inh <- openFile location ReadMode
    mainloop inh list
    hClose inh

mainloop inh list = do 
    ineof <- hIsEOF inh
    if ineof
        then return ()
        else do 
            inpStr <- hGetLine inh
            inpStr:list
            mainloop inh list

Предполагается, что он получает каждую строку (я знаю, что он получает каждую строку, поскольку замена «inpStr: list» на «putStrLn inpStr» работает правильно, отображая все строки) и вставляет его в список, но я получаю следующую ошибку :

Couldn't match expected type `IO' against inferred type `[]'

Вероятно, потому что hGetLine - это не строка, а строка ввода-вывода, с которой я понятия не имею, как обрабатывать, чтобы получить правильную строку, которую я могу вставить в свой список. Я понятия не имею, как это можно решить или в чем конкретно проблема, но если кто-нибудь знает, как правильно поместить каждую строку в файле в список, я был бы признателен.

Заранее спасибо!

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
0
9 889
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В строке, где происходит ошибка, Haskell ожидает «IO a», но вы даете ему []. Значительно упрощая ситуацию, в блоке do монады ввода-вывода каждая строка либо:

  • Что-то, что возвращает значение типа «IO a»; значение типа «а» в нем отбрасывается (поэтому «а» часто бывает «()»)
  • Выражение <-, которое делает то же самое, но вместо того, чтобы отбрасывать значение типа "a", дает ему имя слева от <-
  • Let, который делает не что иное, как дает имя значению.

В этом блоке do "hGetLine inh" возвращает "IO String", а строка внутри него извлекается и получает имя inpStr. Следующая строка, поскольку это не let и не <-, должна иметь тип «IO a», которого нет (что вызывает ошибку компилятора). Что вы можете сделать вместо этого, поскольку у вас уже есть String, это let:

let list' = inpStr:list

Это создает новый список, состоящий из строки, за которой следует исходный список, и дает ему имя «список».

Измените следующую строку, чтобы использовать «список» вместо «список» (таким образом передавая ей новый список). Эта строка вызывает (рекурсивно) mainloop, который прочитает еще одну строку, вызовет себя и так далее. После прочтения всего файла он вернет что-то с типом «IO ()». Этот «IO ()» будет возвращен блоку do в loadNums. Поздравляем, вы только что создали список со строками, прочитанными из файла, в обратном порядке (так как вы добавляли его в начало списка), а затем ничего не сделали с ним.

Если вы хотите что-то с ним сделать, измените "return ()" на "return list"; возврат будет генерировать значение типа «IO [String]» со списком внутри него (return не делает ничего, кроме инкапсуляции значения), которое вы можете извлечь в loadNums с помощью синтаксиса <-.

Остальное оставим читателю в качестве упражнения.

Если это не домашнее задание или что-то в этом роде, нет причин прилагать столько усилий. Повторное использование лениво!

getLines = liftM lines . readFile

main = do
    list <- getLines "dictionary.txt"
    mapM_ putStrLn list

Но поскольку вы, кажется, все еще изучаете Haskell, вам важно понимать, что написал CesarB.

Объясняя вещи кому-то, кто, кажется, все еще изучает Haskell, я бы избегал использовать (или даже показывать) бессмысленный стиль. Не хочу их пугать ;-)

CesarB 19.10.2008 05:51

+1 за упоминание readFile. Так полезно избавиться от императивного мышления: открыть файл / прочитать строку / обнаружить EOF / закрыть файл, и позволить функциям вроде readFile, getContents и interact справиться с беспорядком за вас.

Nefrubyr 15.02.2010 14:36

Что означает "mapM_"? Я знаю, что "mapM" отображает функцию в монаду ... но та, что с подчеркиванием?

Andriy Drozdyuk 03.12.2011 10:40

@drozzy: mapM_ точно такой же, как mapM, за исключением того, что игнорирует результат. Поэтому вместо того, чтобы получать список результатов в монаде, вы просто вставляете в него (). Поскольку putStrLn в любом случае дает IO (), с mapM вы получите IO [()]; mapM_ просто превращает это в IO (), который вам действительно нужен.

Tikhon Jelvis 03.12.2011 16:10

Я знаю, это было какое-то время, но только что наткнулся на это. Я тоже новичок в haskell, и у меня есть вопрос. При вызове кода main просто выводит на экран содержимое dictionary.txt. Как я могу включить их в список, чтобы я мог делать другие вещи с этим списком? main имеет тип :: IO (). Как мне получить список строк, где каждая строка является строкой файла dictionary.txt? Спасибо

Max 05.02.2012 20:29

@Max Определенный здесь getLines :: String -> IO [String] подойдет.

ephemient 06.02.2012 20:33

Другие вопросы по теме