Sco*_*ott 5 io monads recursion haskell lazy-evaluation
在Haskell中,我可以轻松定义一个递归函数,它接受一个值并返回一个字符串:
Prelude> let countdown i = if (i > 0) then (show i) ++ countdown (i-1) else ""
Prelude> countdown 5
"54321"
Run Code Online (Sandbox Code Playgroud)
我想使用相同类型的设计从文件句柄中读取可用数据.在这种特殊情况下,我需要以与hGetContents相同的方式读取数据,但不要将句柄保留在"半封闭"状态,以便我可以循环与使用createProcess打开的进程的stdin/stdout句柄的交互:
main = do
-- do work to get hin / hout handles for subprocess input / output
hPutStrLn hin "whats up?"
-- works
-- putStrLn =<< hGetContents hout
putStrLn =<< hGetLines hout
where
hGetLines h = do
readable <- hIsReadable h
if readable
then hGetLine h ++ hGetLines h
else []
Run Code Online (Sandbox Code Playgroud)
给出错误:
Couldn't match expected type `IO b0' with actual type `[a0]'
In the expression: hGetLine h : hGetLines h
Run Code Online (Sandbox Code Playgroud)
我知道有各种各样的库可用于完成我想要完成的任务,但是我正在学习我的问题是如何执行递归IO.TIA!
Don*_*art 11
天真的解决方案,严格和O(n)堆栈
您仍然必须使用do -notation,这将导致:
import System.IO
import System.IO.Unsafe (unsafeInterleaveIO)
-- Too strict!
hGetLines :: Handle -> IO [String]
hGetLines h = do
readable <- hIsReadable h
if readable
then do
x <- hGetLine h
xs <- hGetLines h
return (x:xs)
else return []
Run Code Online (Sandbox Code Playgroud)
但是看到我的评论,这个版本hGetLines太严格了!
懒惰的流媒体版本
在完成所有输入之前,它不会返回您的列表.你需要一些更懒的东西.为此,我们有unsafeInterleaveIO,
-- Just right
hGetLines' :: Handle -> IO [String]
hGetLines' h = unsafeInterleaveIO $ do
readable <- hIsReadable h
if readable
then do
x <- hGetLine h
xs <- hGetLines' h
return (x:xs)
else return []
Run Code Online (Sandbox Code Playgroud)
现在,您可以逐行开始将结果流式传输到您的消费者代码:
*Main> hGetLines' stdin
123
["123"345
,"345"321
,"321"^D^CInterrupted.
Run Code Online (Sandbox Code Playgroud)
如果你检查(++)ghci 的类型你得到:
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)
这意味着您只能将列表附加在一起(请记住,这String是一个别名[Char],所以它是一个列表).hGetLine的类型是Handle -> IO String,类型hGetLines应该是,IO [String]所以你不能附加这些值.(:)有类型a -> [a],在这里工作得更好.
if readable
then do
-- First you need to extract them
a <- hGetLine h
b <- hGetLines h
-- a and b have type String
-- Now we can cons them and then go back into IO
return (a : b)
Run Code Online (Sandbox Code Playgroud)
这同样适用else [].您需要IO [String]返回一个类型的值.将其更改为return []
此外,你不能只是给你putStrLn的线(=<< hGetLines h),[String]而不是String你putStrLn期望的线.这可以通过几种方式解决.一个是首先结合价值观.putStrln . concat =<< (hGetLines h).或者您可以使用打印每一行mapM_ putStrLn (hGetLines h).
| 归档时间: |
|
| 查看次数: |
2926 次 |
| 最近记录: |