读完文件后我有IO [Char],但我需要[IO Char]

Nob*_*bbZ 1 monads haskell

我有一个number.txt包含大量数字的文件,我把它读成IO String这样的:

readNumber = readFile "number.txt" >>= return
Run Code Online (Sandbox Code Playgroud)

在另一个函数中,我想创建一个Ints 列表,每个数字一个Int ...

让我们假设内容number.txt是:

1234567890
Run Code Online (Sandbox Code Playgroud)

然后我希望我的功能返回[1,2,3,4,5,6,7,8,9,0].

我试图severall版本的map,mapM(_),liftM,和,和和,但我得到了一些错误信息,每次,我是能够减少

Couldn't match expected type `[m0 Char]'
            with actual type `IO String'
Run Code Online (Sandbox Code Playgroud)

我在磁盘上的最后一个版本如下:

module Main where

import Control.Monad
import Data.Char (digitToInt)

main = intify >>= putStrLn . show

readNumber = readFile "number.txt" >>= return

intify = mapM (liftM digitToInt) readNumber
Run Code Online (Sandbox Code Playgroud)

所以,据我所知,错误,我需要一些接受IO [a]和返回的功能[IO a],但我无法用hoogle找到这样的东西...只有相反的方式才能存在

J. *_*son 7

在这里除了其他伟大的答案,这是很好的谈如何阅读 [IO Char]对IO [Char].特别是,你称之为[IO Char]"(延期)IO行动的(直接)列表,它产生Chars"和IO [Char]"a(延迟)IO行动,产生一个Chars 列表".

重要的部分是上面"延迟"的位置---类型IO a和类型之间的主要区别在于a前者最好被认为是在运行时执行的一组指令,最终产生一个a......而后者就是这样a.

这一阶段的区分是理解IO价值观如何运作的关键.值得注意的是,它在程序中非常流畅 - 功能类似fmap或(>>=)允许我们隐藏相位区分.例如,请考虑以下功能

foo :: IO Int         -- <-- our final result is an `IO` action
foo = fmap f getChar where  -- <-- up here getChar is an `IO Char`, not a real one
  f :: Char -> Int
  f = Data.Char.ord   -- <-- inside here we have a "real" `Char`
Run Code Online (Sandbox Code Playgroud)

在这里,我们通过使用一个函数foo修改延迟动作(getChar)来构建延迟动作(),该函数查看在延迟IO动作运行后才出现的世界.


所以,让我们打结这个结,回到手头的问题.为什么你不能IO [Char]变成一个[IO Char](以任何有意义的方式)?好吧,如果你正在查看一段可以访问的代码,IO [Char]那么你要做的第一件事就是偷偷溜进去做那个IO动作

floob = do chars <- (getChars :: IO [Char])
           ...
Run Code Online (Sandbox Code Playgroud)

因为我们已经"进入" 行动...,chars :: [Char]所以我们可以访问的部分中的位置.这意味着到目前为止,我们必须已经运行了生成该字符列表所需的任何运行时操作.我们已经让猫离开了monad并且我们无法以任何有意义的方式重新获得它,因为我们无法返回并"解读"每个角色.IOgetChars

(注意:我一直说"以任何有意义的方式",因为我们绝对可以让猫回到monad使用return,但这不会让我们回到过去,并且从来没有让它们出现在那里.那艘船已经航行了. )

那我们怎么得到一个类型[IO Char]?好吧,我们必须知道(不运行IO)IO我们想做什么样的操作.例如,我们可以编写以下内容

replicate 10 getChar :: [IO Char]
Run Code Online (Sandbox Code Playgroud)

并立即做类似的事情

take 5 (replicate 10 getChar)
Run Code Online (Sandbox Code Playgroud)

没有运行IO动作---我们的列表结构立即可用,并且在运行时有机会到达之前不会延迟.但请注意,我们必须确切知道IO我们要为创建类型而执行的操作的结构[IO Char].也就是说,我们可以使用另一个级别IO来窥视现实世界,以确定我们行动的参数

do len <- (figureOutLengthOfReadWithoutActuallyReading :: IO Int)
   return $ replicate len getChar
Run Code Online (Sandbox Code Playgroud)

这个片段有类型IO [IO Char].要运行它,我们必须IO 两次执行,我们必须让运行时执行两个 IO操作,首先确定长度,然后第二个实际操作我们的IO Char操作列表.

sequence :: [IO a] -> IO [a]
Run Code Online (Sandbox Code Playgroud)

上述函数sequence是执行包含一系列IO动作序列的一些结构的常用方法.我们可以用它来做我们的两阶段读取

twoPhase :: IO [Char]
twoPhase = do len <- (figureOutLengthOfReadWithoutActuallyReading :: IO Int)
              putStrLn ("About to read " ++ show len ++ " characters")
              sequence (replicate len getChar)

>>> twoPhase
Determining length of read
About to read 22 characters
let me write 22 charac"let me write 22 charac"
Run Code Online (Sandbox Code Playgroud)


Mar*_*189 6

你混淆了一些东西:

readNumber = readFile "number.txt" >>= return
Run Code Online (Sandbox Code Playgroud)

回归是不必要的,只是把它留下来.

这是一个工作版本:

module Main where

import Data.Char (digitToInt)

main :: IO ()
main = intify >>= print

readNumber :: IO String
readNumber = readFile "number.txt"

intify :: IO [Int]
intify = fmap (map digitToInt) readNumber
Run Code Online (Sandbox Code Playgroud)

  • 至于为什么`>> = return`是流畅的:这个身份是[monad law]之一(http://www.haskell.org/haskellwiki/Monad_laws). (3认同)