如何使Haskell程序显示初步结果以响应用户输入?

时间:2016-09-01 15:07:29

标签: haskell

我正在Haskell中编写一个程序,它反复获取最新结果并使用它来计算下一个结果。我希望能够看到响应用户输入的最新结果,所以我尝试了这样的事情:

main = mainhelper 0

mainhelper count = do
 count <- return (count + 1)
 line <- getLine
 if null line
  then do mainhelper count
  else do
   putStrLn $ show count
   return ()

我希望如果用户没有输入任何东西,getLine将返回一个空行,但这不会发生,而是程序在收到用户输入之前什么都不做。有办法解决这个问题吗?

2 个答案:

答案 0 :(得分:6)

一个简单的解决方案是为复杂的计算分叉线程,并通过MVar与主UI线程进行通信。例如:

import Control.Exception
import Control.Monad
import Control.Concurrent

thinkReallyHard x = do
    threadDelay 1000000 -- as a proxy for something that's actually difficult
    evaluate (x+1)

main = do
    v <- newMVar 0
    forkIO (forever (modifyMVar_ v thinkReallyHard))
    forever (getLine >> readMVar v >>= print)

您可能想知道evaluatethinkReallyHard中的作用。微妙的是MVar是懒惰的 - 它们可以像计算值一样容易地包含thunk。特别是,这意味着很容易意外地将所有纯计算从分叉线程推送到正在读取并使用MVar内容的线程中。对evaluate的调用只是强制分叉线程在写入MVar之前完成纯计算。

答案 1 :(得分:0)

如果您在不输入文本的情况下按Enter键,它会返回一个空行 - 您只需立即提示输入更多内容,因此可能看起来没有任何结果。但如果您运行该程序,请按Enter键三次,然后输入非空的内容,您将看到最终count反映了多个条目。

这是您的代码的修改版本,它执行相同的操作,但稍微更规范:

main = mainhelper 0

mainhelper count = do
  let count' = count + 1
  line <- getLine
  if null line
    then mainhelper count'
    else print count'

而不是count <- return (count + 1),你可以写let count' = count + 1 - 这是一个纯粹的绑定,而不是需要调用IO monad的东西(就像你正在做的那样{ {1}}和<-)。但我使用return而不是count',否则会创建一个递归绑定。 count - 后缀是&#34;修改版本的标准惯用语&#34;标识符。

接下来,我将'切换为putStrLn . show,这是print的一部分,并且正是如此。

我摆脱了Prelude,因为return ()(和print)已经有putStrLn类型。这样您就可以忽略IO ()语句,因为do的每个分支中都有一个IO表达式。

我不清楚你在这里尝试做什么与你正在做的事情有什么不同 - 代码确实(用命令性的术语)每次用户按下时都会增加一个计数器输入,并在每次用户输入一些非空文本时显示计数器的状态。

这是另一个版本,每次打印计数器,但只在提示时递增,这可能对您有帮助,也可能没有帮助:

if

我使用后续函数main = mainhelper 0 mainhelper count = do print count line <- getLine mainhelper (if null line then count else succ count) ,而不是显式succ,这只是一种样式首选项。