为什么我的IO没有按顺序执行?

5

我遇到了一个IO执行顺序不正确的问题,即使在do结构内部也是如此。

在下面的代码中,我只是跟踪哪些卡片还剩下,其中卡片是字符元组(一张牌的花色和点数各为一个字符),然后用户会被连续询问哪些卡片已经打出。我希望putStr在每次输入之间执行,并且不像现在这样在最后执行。

module Main where
main = doLoop cards
doLoop xs = do  putStr $ show xs
                s <- getChar
                n <- getChar
                doLoop $ remove (s,n) xs
suits = "SCDH"
vals = "A23456789JQK"
cards = [(s,n) | s <- suits, n <- vals]
type Card = (Char,Char)
remove :: Card -> [Card] -> [Card]
remove card xs = filter (/= card) xs
3个回答

14
如果我的想法正确的话,那么你的问题是Haskell的IO是缓冲的:这个问题解释了正在发生的事情。当你运行编译过的Haskell程序时,GHC将输出存储在缓冲区中,并只有在缓冲区太满、打印换行符或调用hFlush stdout时才定期刷新到屏幕上。
你可能看到的另一个问题是,getChar可能不会触发,直到读取到一个换行符,但是换行符在你的输入流中;你可以通过额外的getChar来消耗掉换行符,但应该有更好的方法。

没错,做得好。我自己尝试了一下,在GHCi中很好用,在编译时直到最后才打印,就像你链接到的问题一样。 - C. A. McCann

8

absz的回答是正确的,Haskell的缓冲IO是导致问题的原因。以下是一种重写doLoop以达到您想要的效果的方法:

doLoop xs = do  putStrLn $ show xs
                input <- getLine
                let s:n:_ = input
                doLoop $ remove (s,n) xs

两个变化:使用putStrLn附加换行符并刷新输出(这可能是您想要的),并使用getLine一次获取输入的一行(同样,这可能是您想要的)。

糟糕的代码!你在这里引入了潜在的模式匹配失败。 - David V.
是的,这个案例远非详尽无遗,但就IO操作而言,它已经达到了概念验证的目的。 - perimosocordiae

6

正如其他人指出的那样,使用putStr形式的缓冲是您的问题。

另外,一个风格上的建议: putStrLn $ show xsprint xs 是相同的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接