Haskell中的递归IO
在Haskell中,我可以轻松定义一个递归函数,该函数接受一个值并返回一个字符串:Haskell中的递归IO,haskell,recursion,io,monads,lazy-evaluation,Haskell,Recursion,Io,Monads,Lazy Evaluation,在Haskell中,我可以轻松定义一个递归函数,该函数接受一个值并返回一个字符串: Prelude> let countdown i = if (i > 0) then (show i) ++ countdown (i-1) else "" Prelude> countdown 5 "54321" 我希望使用相同的设计从文件句柄读取可用数据。在这种特殊情况下,我需要以与hGetContents相同的方式读取数据,但不要将句柄保持在“半封闭”状态,以便我可以循环与使用creat
Prelude> let countdown i = if (i > 0) then (show i) ++ countdown (i-1) else ""
Prelude> countdown 5
"54321"
我希望使用相同的设计从文件句柄读取可用数据。在这种特殊情况下,我需要以与hGetContents相同的方式读取数据,但不要将句柄保持在“半封闭”状态,以便我可以循环与使用createProcess打开的进程的stdin/stdout句柄的交互:
main=do
--为子流程输入/输出获取hin/hout句柄
“怎么了?”
--工作
--putStrLn=这意味着部分代码期望hGetLines h具有类型IO a
,另一部分发现它具有类型[a]
。您可能希望您的if语句是:
if readable
then return hGetLine h ++ hGetLines h
else return []
如果您在ghci中检查(++)
的类型,您会得到:
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]
这意味着您只能将列表附加在一起(请记住,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)
如果可读
那就做吧
--首先你需要提取它们
一个简单的解决方案,严格的和O(n)堆栈
您仍然必须使用do符号,这将导致:
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 []
现在,您可以开始将结果逐行流式传输到您的消费代码:
*Main> hGetLines' stdin
123
["123"345
,"345"321
,"321"^D^CInterrupted.
你的代码有点奇怪。。。它甚至不编译。这样如何:如果可读,那么hGetLine>=\a->hGetLine>=\b->返回$a+b,否则返回[]
?另一个问题是,这不会流式传输。你是不是想在第二次调用中调用hGetLines
。错过了递归调用,因此应改为使用:
。请注意,此示例不会流式处理,而是使用O(n)堆栈。
-- 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 []
*Main> hGetLines' stdin
123
["123"345
,"345"321
,"321"^D^CInterrupted.