如何将 Haskell 的 readFile 函数用于多个文件



我对IO有一点了解。我知道可以使用readFile来获取文件的内容。例如:

main = do
let inputFilePath = "C:\Haskell\myawesomeprogram\files\a.txt"
content <- readFile inputFilePath
print content

调用程序:

> runghc myawesomeprogram
"AAA"

太棒了,真管用!现在我想从多个文件中读取内容。我试过这样的东西:

files = ["C:\Haskell\myawesomeprogram\files\a.txt", "C:\Haskell\myawesomeprogram\files\b.txt","C:\Haskell\myawesomeprogram\files\c.txt"]

main :: IO ()
main = do
filesContent <- readFiles files
print filesContent
readFiles (x:xs) = do 
content <- readFile x
content : readFiles xs

这将给我以下错误消息:

myawesomeprogram.hs:6:21: error:
* Couldn't match type `[]' with `IO'
Expected type: IO String
Actual type: [String]
* In a stmt of a 'do' block: filesContent <- readFiles files
In the expression:
do filesContent <- readFiles files
print filesContent
In an equation for `main':
main
= do filesContent <- readFiles files
print filesContent
|
6 |     filesContent <- readFiles files
|                     ^^^^^^^^^^^^^^^
myawesomeprogram.hs:9:1: error:
Couldn't match type `IO' with `[]'
Expected type: [FilePath] -> [String]
Actual type: [FilePath] -> IO String
|
9 | readFiles (x:xs) = do
| ^^^^^^^^^^^^^^^^^^^^^^^...
myawesomeprogram.hs:11:5: error:
* Couldn't match type `[]' with `IO'
Expected type: IO String
Actual type: [String]
* In a stmt of a 'do' block: content : readFiles xs
In the expression:
do content <- readFile x
content : readFiles xs
In an equation for `readFiles':
readFiles (x : xs)
= do content <- readFile x
content : readFiles xs
|
11 |     content : readFiles xs
|     ^^^^^^^^^^^^^^^^^^^^^^

我做错了什么,但是,我找不到正确的方法。你能用正确的方法吗?

readFiles xs不是一个列表,因此不能在其中预先添加项目。相反,它是一个操作,执行时会生成一个列表。

更具体地说,readFiles xs的类型是IO [String](IO是可执行动作的类型(,而列表的类型将是[String]。这就是错误消息告诉您的内容:无法将类型IO [String][String]匹配。

因此,要获得列表,您必须执行操作,就像执行readFile一样

readFiles (x:xs) = do
content <- readFile x
theRest <- readFiles xs
pure (content : theRest)

还要注意,当readFiles的参数是空列表时,它不知道该怎么办。你应该在编译时得到一个关于它的警告,如果你不修复它,你会在运行时崩溃。

要修复,只需为空列表添加一个等式:

readFiles [] = pure []
readFiles (x:xs) = do
content <- readFile x
theRest <- readFiles xs
pure (content : theRest)

如果您想对列表中的每一项都做些什么,可以使用map

但是,列表上的map readFile files只会为您提供一个将产生其内容的操作列表,而不是一个内容列表。然后是sequence,它将采取一系列行动,并将其转化为一个行动,产生一系列行动的结果。映射一个动作并对其进行排序是一个非常常见的问题,因此它被缩短为mapM。它位于Control.Monad

你想要的代码是这样的:

main :: IO ()
main = do
filesContent <- mapM readFile files
print filesContent

请不要滚动您自己的函数来执行此操作。

最新更新