如何不顾Haskell的懒惰输出进度信息?
How to output progress information in spite of Haskell's laziness?
今天我希望 Haskell 表现得像任何命令式语言,看看这个:
import Data.HashMap.Strict as HashMap
import Data.Text.IO
import Data.Text
import Data.Functor ((<&>))
putStr "Reading data from file ..."
ls <- lines <$> readFile myFile
putStrLn " done."
putStr "Processing data ..."
let hmap = HashMap.fromList $ ls <&> \l -> case splitOn " " l of
[k, v] -> (k, v)
_ -> error "expecting \"key value\""
putStrLn " done."
基本上,用户应该知道此刻程序在做什么。这段代码的结果是
的立即输出
> Reading data from file ... done.
> Sorting data ... done.
...然后它开始做实际工作,输出结果违背了它的目的。
我很清楚这是一项功能。 Haskell 是声明性的,评估顺序由实际依赖项决定,而不是由我的 .hs 文件中的行号决定。因此我尝试了以下方法:
putStr "Reading data from file ..."
lines <- lines <$> readFile myFile
putStrLn $ lines `seq` " done."
putStr "Processing data ..."
let hmap = HashMap.fromList $ ls <&> \l -> case splitOn " " l of
[k, v] -> (k, v)
_ -> error "expecting \"key value\""
putStrLn $ hmap `seq` " done."
想法:seq
仅 returns 一旦其第一个参数被评估为 Weak Head Normal Form。它有效,有点。我的程序的输出现在暂时没有,然后,一旦工作完成,就会发生所有 IO。
有解决办法吗?
编辑:我更改了问题以回复 Ben 的回答。导入现在应该更有意义并且程序真正运行。
DanielWagner 评论了这个相关问题:
GHCi and compiled code seem to behave differently
确实解决了我的问题。
putStrLn $ hmap `seq` " done."
做的正是它应该做的。我只是缺少冲洗标准输出。所以这实际上满足了我的需要:
putStr "Reading data from file ..."
hFlush stdout -- from System.IO
lines <- lines <$> readFile myFile
putStrLn $ lines `seq` " done."
putStr "Processing data ..."
hFlush stdout
let hmap = HashMap.fromList $ ls <&> \l -> case splitOn " " l of
[k, v] -> (k, v)
_ -> error "expecting \"key value\""
putStrLn $ hmap `seq` " done."
你没有给我们你所说的具有这种行为的实际代码:
The output of my program is now nothing for a while and then, once the work as been done, all the IO occurs.
我怎么知道这不是您运行宁的代码?您的代码根本无法编译为 运行!几个问题:
- 您从
lines
收到类型错误,因为它属于标准 Prelude
,但该版本适用于 String
,而您正在使用 Text
。
- 您还没有从任何地方导入
splitOn
- 要导入的明显
splitOn
来自 Data.Text
,但其类型为 Text -> Text -> [Text]
,即 returns a list Text
在分隔符的所有出现处拆分。您显然期待一对,仅在第一个分隔符处拆分。
因此,在 非常 的最低限度,这是您在 运行 中 ghci
中 imports/definitions 未显示的代码我们
尽可能少地更改它并将其设置为 运行 给了我这个:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text.IO as StrictIO
import qualified Data.Text as Text
myFile = "data.txt"
main = do
putStr "Reading data from file ..."
lines <- Text.lines <$> StrictIO.readFile myFile
putStrLn $ lines `seq` " done."
putStr "Processing data ..."
let hmap = HashMap.fromList $ Text.breakOn " " <$> lines
putStrLn $ hmap `seq` " done."
我生成了一个包含 5,000,000 行的非常简单的数据文件和 运行 带有 runhaskell foo.hs
的程序,实际上 [=] 的 appea运行ce 之间有明显的停顿53=] 消息和每行出现的“完成”。
我看不出为什么所有的 IO 都会被延迟一次出现(包括第一个 putStrLn
的结果)。你实际上是如何 运行 宁这个代码(或者更确切地说,完整 and/or 不同的代码实际上 运行s)?在 post 中,您将其编写为 GHCi 的输入而不是完整的程序(根据导入和 IO
判断同一级别的语句,没有 do
块或任何顶级函数的定义)。我唯一的想法是,也许您的数据文件要小得多,以至于处理时间几乎无法察觉,并且ghci
或 runhaskell
对 Haskell 代码本身的初始启动处理是唯一明显的延迟;然后我可以想象在打印所有消息之后会有轻微的延迟一次。
今天我希望 Haskell 表现得像任何命令式语言,看看这个:
import Data.HashMap.Strict as HashMap
import Data.Text.IO
import Data.Text
import Data.Functor ((<&>))
putStr "Reading data from file ..."
ls <- lines <$> readFile myFile
putStrLn " done."
putStr "Processing data ..."
let hmap = HashMap.fromList $ ls <&> \l -> case splitOn " " l of
[k, v] -> (k, v)
_ -> error "expecting \"key value\""
putStrLn " done."
基本上,用户应该知道此刻程序在做什么。这段代码的结果是
的立即输出> Reading data from file ... done.
> Sorting data ... done.
...然后它开始做实际工作,输出结果违背了它的目的。
我很清楚这是一项功能。 Haskell 是声明性的,评估顺序由实际依赖项决定,而不是由我的 .hs 文件中的行号决定。因此我尝试了以下方法:
putStr "Reading data from file ..."
lines <- lines <$> readFile myFile
putStrLn $ lines `seq` " done."
putStr "Processing data ..."
let hmap = HashMap.fromList $ ls <&> \l -> case splitOn " " l of
[k, v] -> (k, v)
_ -> error "expecting \"key value\""
putStrLn $ hmap `seq` " done."
想法:seq
仅 returns 一旦其第一个参数被评估为 Weak Head Normal Form。它有效,有点。我的程序的输出现在暂时没有,然后,一旦工作完成,就会发生所有 IO。
有解决办法吗?
编辑:我更改了问题以回复 Ben 的回答。导入现在应该更有意义并且程序真正运行。
DanielWagner 评论了这个相关问题:
GHCi and compiled code seem to behave differently
确实解决了我的问题。
putStrLn $ hmap `seq` " done."
做的正是它应该做的。我只是缺少冲洗标准输出。所以这实际上满足了我的需要:
putStr "Reading data from file ..."
hFlush stdout -- from System.IO
lines <- lines <$> readFile myFile
putStrLn $ lines `seq` " done."
putStr "Processing data ..."
hFlush stdout
let hmap = HashMap.fromList $ ls <&> \l -> case splitOn " " l of
[k, v] -> (k, v)
_ -> error "expecting \"key value\""
putStrLn $ hmap `seq` " done."
你没有给我们你所说的具有这种行为的实际代码:
The output of my program is now nothing for a while and then, once the work as been done, all the IO occurs.
我怎么知道这不是您运行宁的代码?您的代码根本无法编译为 运行!几个问题:
- 您从
lines
收到类型错误,因为它属于标准Prelude
,但该版本适用于String
,而您正在使用Text
。 - 您还没有从任何地方导入
splitOn
- 要导入的明显
splitOn
来自Data.Text
,但其类型为Text -> Text -> [Text]
,即 returns a listText
在分隔符的所有出现处拆分。您显然期待一对,仅在第一个分隔符处拆分。
因此,在 非常 的最低限度,这是您在 运行 中 ghci
中 imports/definitions 未显示的代码我们
尽可能少地更改它并将其设置为 运行 给了我这个:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text.IO as StrictIO
import qualified Data.Text as Text
myFile = "data.txt"
main = do
putStr "Reading data from file ..."
lines <- Text.lines <$> StrictIO.readFile myFile
putStrLn $ lines `seq` " done."
putStr "Processing data ..."
let hmap = HashMap.fromList $ Text.breakOn " " <$> lines
putStrLn $ hmap `seq` " done."
我生成了一个包含 5,000,000 行的非常简单的数据文件和 运行 带有 runhaskell foo.hs
的程序,实际上 [=] 的 appea运行ce 之间有明显的停顿53=] 消息和每行出现的“完成”。
我看不出为什么所有的 IO 都会被延迟一次出现(包括第一个 putStrLn
的结果)。你实际上是如何 运行 宁这个代码(或者更确切地说,完整 and/or 不同的代码实际上 运行s)?在 post 中,您将其编写为 GHCi 的输入而不是完整的程序(根据导入和 IO
判断同一级别的语句,没有 do
块或任何顶级函数的定义)。我唯一的想法是,也许您的数据文件要小得多,以至于处理时间几乎无法察觉,并且ghci
或 runhaskell
对 Haskell 代码本身的初始启动处理是唯一明显的延迟;然后我可以想象在打印所有消息之后会有轻微的延迟一次。