在readFile之后使用行

时间:2012-09-25 20:05:41

标签: haskell readfile lines

我正在尝试在Haskell中进行一些编程。我正在尝试读取一个文件,然后使用line函数将文件中的每一行放在一个列表中。这是部分代码:

file = "muh.rtr"
readTrack :: String -> Track
readTrack file =
    do      let defFile = readFile file
            let fileLines = lines defFile

但是,我一直收到这个错误:

Parser.hs:22:39:
    Couldn't match expected type `String' with actual type `IO String'
    In the first argument of `lines', namely `defFile'
    In the expression: lines defFile
    In an equation for `fileLines': fileLines = lines defFile

我一直在互联网上搜索几个小时,希望能在某处找到答案,但到目前为止我还没那么幸运。

5 个答案:

答案 0 :(得分:9)

你可能想要这样的东西:

readTrack :: String -> IO Track
readTrack file = do defFile <- readFile file
                    let fileLines =  lines defFile
                    -- etc....

......或类似的东西:

readTrack :: String -> IO Track
readTrack file = do fileLines <- liftM lines (readFile file)
                    -- etc....

但是你

将完全由非常简单的错误组成的代码提供给GHC,然后在Stack Overflow上发布错误消息不是一种好的学习方法。

答案 1 :(得分:6)

readFile的类型是

readFile :: FilePath -> IO String 

因此您需要使用<-来绑定结果,并且您的函数必须返回IO Track

readTrack :: String -> IO Track
readTrack file =
  do defFile <- readFile file
     let fileLines = lines defFile
     ...

我建议在Haskell中阅读关于IO的好教程,例如Input and Output chapter of Learn You a Haskell for Great Good!

答案 2 :(得分:5)

readFile返回IO string。也就是说,它是一个返回字符串的IO计算。这意味着您需要使用<-代替let来“获取”其返回的字符串。

 readTrack file =
    do
        defFile <- readFile file
        ...

您可以使用let绑定非IO计算的内容,例如行的返回值,即常规字符串。

readTrack file =
    do
        defFile <- readFile file
        let fileLines = lines defFile
        ...

最后,您需要返回您可能想要尝试的值

readTrack file =
    do
        defFile <- readFile file
        let fileLines = lines defFile
        fileLines --Doesn't actually work!

但不幸的是,由于我们在“do”块中并且正在尝试返回monadic计算,我们需要将fileLines发送回io monad(记住,out函数返回IO [String],而不是{{ 1}}!

String

请注意,这里的“返回”是不是一个返回语句,在大多数语言中都可以找到,并且它不应该用在纯函数中。

这一切看起来好像很多。我建议你坚持使用纯函数(没有输入和输出/ monad),直到你得到更好的语言为止。

答案 3 :(得分:4)

你不能这样做 - 你已经遇到了IO monad。你需要做的是:

readTrack :: String -> IO Track
readTrack file = do
   defFile <- readFile file
   let fileLines = lines deffile
   ...
   return whatever

IO T值视为语句(而不是表达式),返回类型为T。因为语句有副作用,但表达式没有副作用,所以你永远不能将语句变成表达式;类型系统强制执行此操作,这就是您的类型签名不起作用的原因。

请注意do块中不同的类似分配的语法:在此示例中,foo <- bar用于IO操作,而let baz = quux语法用于纯粹的功能评估。使用monadic I / O会产生更多影响 - 它在Haskell的多态类型系统的完整通用性中更有意义,但是,对于纯粹的与副作用操作的语法指标,它也不一定是坏的。

通常,最好将大部分实现保留在纯函数域中:使用常规函数方法实现纯计算,然后在{{{I> O操作中描述您的I / O操作1}} monad。在IO monad中编写循环是一个常见的新手错误,它更适合作为列表推导或递归函数。

答案 4 :(得分:3)

如果您的函数应该具有类型readTrack :: String -> Track,您确定String是文件名吗?也许是数据 - 如果是这样,请不要使用readFile。写一些样本数据并使用它进行测试,例如

sampleData = "2 3\n1 30 234 45\n1 2 32 4\n5 3 4 23"

(关于这个家庭作业的另一个问题没有使用文件IO。我不会链接到它,因为你处于危机中并且可能想要复制,并且无论如何你拒绝学习haskell至少我会强迫你提高你的StackOverflow搜索技能!:))

在任何情况下,我认为通过解决字符串问题比通过解决IO问题获得更多分数。

延迟readFile问题,直到您的纯版本正常工作,否则您最终可能会在IO monad中编写大部分代码,这将比必要复杂得多。

你有一个纯函数readTrack :: String -> Track,你可以做

readTrackFrom :: FilePath -> IO Track
readTrackFrom filename = fmap readTrack (readFile filename)

现在,fmap :: Functor f => (a -> b) -> f a -> f b,所以采用纯函数并提升它们在不同的计算环境(如IO)中工作。

由于IOFunctor(明天查看,而不是今晚),我们将其用作(String -> Track) -> IO String -> IO Track类型。这很好,因为readTrack :: String -> Track(readFile filename) :: IO String

如果您愿意,可以根据需要>>= print>>= writeFile newfilename

请勿忘记在使用deriving Show后添加data Track =...,但如果您使用的是type Track = ....,则无需添加。{/ p>