Haskell:将文件中的每一行都插入到列表中

Haskell: Inserting every line from a file into a list

提问人:Sergio Morales 提问时间:10/19/2008 更新时间:10/19/2008 访问量:10408

问:

我目前正在与 Haskell 合作,发现自己遇到了一些麻烦。我应该阅读并将“字典 .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 不是 String,而是一个 IO 字符串,我不知道如何处理它以获得可以插入到列表中的正确字符串。我不知道如何解决这个问题,或者问题到底是什么,但如果有人知道如何正确地将文件中的每一行都放入列表中,我将不胜感激。

提前致谢!

文件 haskell input io

评论


答:

14赞 CesarB 10/19/2008 #1

在发生错误的行中,Haskell 期望“IO a”,但您给它一个 []。简化了很多事情,在 IO monad 上的 do 块上,每一行都是:

  • 返回“IO a”类型值的东西;其中“a”类型的值被丢弃(因此“a”通常是“()”)
  • 一个 <- 表达式,它执行相同的操作,但不是丢弃“a”类型的值,而是将其命名为 <- 的左侧
  • 一个 let,它只不过是给一个值起一个名字

在该 do 块中,“hGetLine inh”返回一个“IO String”,其中的 String 被提取并命名为 inpStr。下一行,因为它既不是 let 也不是 <-,因此应该具有类型“IO a”,但它没有(因此导致编译器错误)。由于您已经有了 String,因此您可以做的是让:

let list' = inpStr:list

这将创建一个新列表,该列表由 String 后跟原始列表组成,并为其命名为“list' ”。

将以下行更改为使用 “list' ” 而不是 “list”(从而将新列表传递给它)。该行(递归地)调用主循环,主循环将再读取一行,调用自身,依此类推。读取整个文件后,它将返回“IO()”类型的内容。这个 “IO ()” 将返回到 loadNums 处的 do 块。恭喜,您刚刚创建了一个列表,其中包含从文件中读取的行,顺序相反(因为您附加到列表的头部),然后没有对它执行任何操作。

如果你想对它做点什么,把“return()”改成“return list”;return 将生成一个类型为“IO [String]”的值,其中包含列表(return 只执行封装值的作用),您可以在 loadNums 中使用 <- 语法提取该值。

其余的留给读者作为练习。

15赞 ephemient 10/19/2008 #2

除非这是为了家庭作业或其他什么,否则没有理由花费这么多精力。重用是懒惰的!

getLines = liftM lines . readFile

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

但是,由于你似乎还在学习Haskell,所以理解CesarB所写的内容对你来说很重要。

评论

0赞 CesarB 10/19/2008
在向那些似乎仍在学习 Haskell 的人解释事情时,我会避免使用(甚至展示)毫无意义的风格。不想吓唬他们;-)
7赞 Nefrubyr 2/15/2010
+1 提及 .摆脱打开文件/读取行/检测 EOF/关闭文件的命令式思维模式,让 等函数为您处理混乱,这非常有帮助。readFilereadFilegetContentsinteract
0赞 Andriy Drozdyuk 12/3/2011
“mapM_”是什么意思?我知道“mapM”将函数映射到 monad..但是带有下划线的那个?
0赞 Tikhon Jelvis 12/3/2011
@drozzy:完全相同,只是忽略了结果。因此,您无需在单子中获取结果列表,而只是在单子中获取结果。既然无论如何给予,你会得到; 只是把它变成你真正想要的。mapM_mapM()putStrLnIO ()mapMIO [()]mapM_IO ()
0赞 Max 2/6/2012
我知道,我已经有一段时间了,但刚刚遇到了它。我也是一个 haskell 初学者,有一个问题。使用您的代码调用 main 只需将 dictionary .txt 的内容打印到屏幕上。我怎样才能把它们放在一个列表中,以便我能够对该列表做其他事情?main 的类型为 :: IO ()。如何获取字符串列表,其中每个字符串都是一行字典 .txt?感谢