Haskell中的递归IO

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

在Haskell中,我可以轻松定义一个递归函数,该函数接受一个值并返回一个字符串:

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.