我有一个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找到这样的东西...只有相反的方式才能存在
在这里除了其他伟大的答案,这是很好的谈如何阅读 [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)
你混淆了一些东西:
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)