提问人:Sergio Morales 提问时间:10/19/2008 更新时间:10/19/2008 访问量:10408
Haskell:将文件中的每一行都插入到列表中
Haskell: Inserting every line from a file into a list
问:
我目前正在与 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 期望“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 中使用 <- 语法提取该值。
其余的留给读者作为练习。
除非这是为了家庭作业或其他什么,否则没有理由花费这么多精力。重用是懒惰的!
getLines = liftM lines . readFile
main = do
list <- getLines "dictionary.txt"
mapM_ putStrLn list
但是,由于你似乎还在学习Haskell,所以理解CesarB所写的内容对你来说很重要。
评论
readFile
readFile
getContents
interact
mapM_
mapM
()
putStrLn
IO ()
mapM
IO [()]
mapM_
IO ()
评论