如何阻止(1)按键之前或(2)达到hh:mm
格式的先前输入时间。我正在使用Windows以防万一。这个DOS assembler program(也可以在Windows上运行)通过Windows控制台中的batchman waittil 16:30
之类的东西做我想要的但是我想在Haskell中完全做到这一点,(没有利用该计划。)
答案 0 :(得分:4)
你可以开始两个线程:一个读取一个字符,另一个等待直到达到指定的时间;他们都写一个MVar
来表示完成。
这有点棘手,但主要是由于细节:我们希望stdin
处于无缓冲和非回显模式,以便单个按键停止等待而不打印任何内容,然后恢复原始状态然后;并且我们还需要在完成后杀死两个线程,以便我们例如在超时到期后停止从stdin
读取。此外,如果发生异常,我们需要确保正确清理事物。 bracket
在这里简化了清理逻辑,但它仍然非常难看:
import Prelude hiding (catch)
import Control.Exception
import Control.Concurrent
import System.IO
withRawStdin :: IO a -> IO a
withRawStdin = bracket uncook restore . const
where
uncook = do
oldBuffering <- hGetBuffering stdin
oldEcho <- hGetEcho stdin
hSetBuffering stdin NoBuffering
hSetEcho stdin False
return (oldBuffering, oldEcho)
restore (oldBuffering, oldEcho) = do
hSetBuffering stdin oldBuffering
hSetEcho stdin oldEcho
waitFor :: Int -> IO ()
waitFor delay = do
done <- newEmptyMVar
withRawStdin . bracket (start done) cleanUp $ \_ -> takeMVar done
where
start done = do
t1 <- forkIO $ getChar >> putMVar done ()
t2 <- forkIO $ threadDelay delay >> putMVar done ()
return (t1, t2)
cleanUp (t1, t2) = do
killThread t1
killThread t2
即便如此,此解决方案仍然无法处理等待特定时间 - 只需等待一定数量的微秒。为了将一天中的时间转换为睡眠的数微秒,this previous SO question可能有所帮助。如果睡眠足够长,那么它们可能不适合Int
微秒,因此您可能必须在循环中使用threadDelay
,或者从delay
使用unbounded-delays封装