haskell 如何在IO函数中尽早转发结果?

jhiyze9q  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(106)

我创建了一系列流程“步骤”,这些步骤的实现方式使尽早输出信息成为可能。
例如

mainFromSettings :: Settings -> IO ()
...
mainFromSettings ... =
    do
        Sys.setInputEcho False
        Sys.hSetBuffering Sys.stdout Sys.NoBuffering
        sContent <- getContents
        let
            records :: [[String]]
            records = Rcr.fromContent rcrConfig sContent
...
        print records

字符串
上面的代码片段在每次从延迟IO流'sContent'构建记录时打印一条记录。
我想这是因为一旦输入了一行,“打印记录”就占据了准备打印的列表的头部。我还假设当列表的顺序颠倒时,这将不起作用。
不幸的是,在其中一个处理步骤中,它不是这样工作的。对于这个处理步骤,记录列表必须在单个字符显示之前完成。这个函数的区别是一个额外的IO函数,为每个记录提供额外的信息。
为了理解这个问题,我创建了下面的代码来模拟这个额外的IO函数。该代码还模拟了已经可用的记录(以避免打开句柄的冲突)。

import qualified System.IO as Sys
import qualified System.IO.Echo as Sys

main :: IO ()

main =
    Sys.setInputEcho False >>
    Sys.hSetBuffering Sys.stdout Sys.NoBuffering >>
    Sys.hSetBuffering Sys.stdin Sys.NoBuffering >>
    checkAll ["12","34","56"] >>=
    print

checkAll :: [String] -> IO [String]
checkAll [] = return []
checkAll (s:lrs) =
    do
        l <- getLine
        let
            symbol = if l == s then "==" else "/="
            p1 = l ++ symbol ++ s
        p2 <- checkAll lrs
        return (p1 : p2)


上面的代码只有在所有三行都完成时才完成。
我也尝试了一个'foldrM'的替代品,但它没有工作,实际上:

import qualified System.IO as Sys
import qualified System.IO.Echo as Sys
import qualified Data.Foldable as Fld

main :: IO ()
main =
    Sys.setInputEcho False >>
    Sys.hSetBuffering Sys.stdout Sys.NoBuffering >>
    Sys.hSetBuffering Sys.stdin Sys.NoBuffering >>
    checkAll [] ["12","34","56"] >>= 
    print

checkAll :: [String] -> [String] -> IO [String]
checkAll = Fld.foldrM f
    where
        f :: String -> [String] -> IO [String]
        f s ls =
            do
                l <- getLine
                if l == s
                    then return (ls ++ [l ++ "==" ++ s])
                    else return (ls ++ [l ++ "/=" ++ s])


也就是说,只有当所有三条线都完成时,它才完成。
只要我删除中间的IO函数,就像这样:

import qualified System.IO as Sys
import qualified System.IO.Echo as Sys

main :: IO ()
main = 
    Sys.setInputEcho False >> 
    Sys.hSetBuffering Sys.stdout Sys.NoBuffering >>
    Sys.hSetBuffering Sys.stdin Sys.NoBuffering >> 
    getContents >>= 
    print . lines


.它像预期的那样工作,即使函数行介于两者之间。
那么,我该怎么做才能实现这种行为呢?使用这种懒惰IO方法是否可能实现?我是否需要管道来实现这一点?
已经审议了以下专题:
Haskell - Read Lines from Handle without blocking

iovurdzv

iovurdzv1#

checkAll确实 * 字面上 * 对dogetLine说,然后在 * return ing任何数据之前递归地做checkAll *。“懒惰”IO不是懒惰求值的自然结果(正常代码免费获得)。这是unsafeInterleaveIO引入的一种神奇的行为。对这个函数的调用隐藏在getContents这样的库函数中。
您可以通过自己调用unsafeInterleaveIO来使checkAll参与惰性IO。

import qualified System.IO as Sys
import qualified System.IO.Echo as Sys
import qualified System.IO.Unsafe as Sys

main :: IO ()
main = do
    Sys.setInputEcho False
    Sys.hSetBuffering Sys.stdout Sys.NoBuffering
    Sys.hSetBuffering Sys.stdin Sys.NoBuffering
    print =<< checkAll ["12", "34", "56"]

checkAll :: [String] -> IO [String]
checkAll [] = return []
checkAll (s : lrs) = do
    l <- getLine
    let symbol = if l == s then "==" else "/="
        p1 = l ++ symbol ++ s
    p2 <- Sys.unsafeInterleaveIO $ checkAll lrs -- just added a call here
    return (p1 : p2)

字符串
我强烈建议使用适当的流库而不是懒惰IO。懒惰IO是一个巧妙的技巧,可以使真正简单的程序比它们“应该”的响应速度更快,但永远不应该被依赖。在这方面,我会指出,你建议的conduit包真的是矫枉过正。你不需要比streamingStream更多的东西,也就是免费的monad Transformer FreeT

相关问题