我想等到用户输入终止EOF然后输出全部.这不是getContents应该做的吗?每次用户点击进入时,以下代码输出,我做错了什么?
import System.IO
main = do
hSetBuffering stdin NoBuffering
contents <- getContents
putStrLn contents
Run Code Online (Sandbox Code Playgroud)
Tik*_*vis 10
基本问题是Lazy IOgetContents的实例.这意味着产生一个thunk,可以像普通的Haskell值一样进行评估,并且仅在强制执行时执行相关的IO .getContents
contents是一个putStr尝试打印的惰性列表,它强制列表并使原因getContents尽可能多地读取.putStr然后打印所有强制的内容,并继续尝试强制列表的其余部分直到它命中[].因为getContents可以读取越来越多的流 - 确切的行为取决于缓冲 - putStr可以立即打印越来越多,给你你看到的行为.
虽然这种行为对于非常简单的脚本很有用,但它将Haskell的评估顺序与可观察的效果联系起来 - 这是它从未打算做的事情.这意味着准确控制contentsget打印的部分是不方便的,因为你必须打破正常的Haskell抽象并准确理解事物的评估方式.
这导致一些可能不直观的行为.例如,如果您尝试获取输入的长度 - 并实际使用它 - 在打印之前强制列表,为您提供所需的行为:
main = do
contents <- getContents
let n = length contents
print n
putStr contents
Run Code Online (Sandbox Code Playgroud)
但如果你移动print n后putStr,你会回到原来的行为,因为n直到打印输入后才会被强制(尽管之前n仍然定义了putStr):
main = do
contents <- getContents
let n = length contents
putStr contents
print n
Run Code Online (Sandbox Code Playgroud)
通常,这种事情不是问题,因为它不会改变代码的行为(虽然它会影响性能).懒惰的IO通过刺穿抽象层将其带入正确的领域.
这也为我们提供了一个如何解决问题的提示:contents在打印之前我们需要一些强制方法.正如我们所看到的,我们可以这样做,length因为length在计算结果之前需要遍历整个列表.我们可以使用seq强制左手表达式与右手表达式同时进行评估而不是打印它,而是抛弃实际值:
main = do
contents <- getContents
let n = length contents
n `seq` putStr contents
Run Code Online (Sandbox Code Playgroud)
与此同时,这仍然有点难看,因为我们length只是用来遍历列表,而不是因为我们真正关心它.我们真正想要的是一个只需遍历列表就可以对其进行评估的函数,而无需做任何其他事情.令人高兴的是,这正是这样deepseq做的(对于许多数据结构,而不仅仅是列表):
import Control.DeepSeq
import System.IO
main = do
contents <- getContents
contents `deepseq` putStr contents
Run Code Online (Sandbox Code Playgroud)
这是懒惰I/O的问题.一个简单的解决方案是使用严格的I/O,例如via ByteStrings:
import qualified Data.ByteString as S
main :: IO ()
main = S.getContents >>= S.putStr
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1535 次 |
| 最近记录: |