我想在Haskell中練習使用IO
monad,所以我決定製作一個「屏幕保護程序」程序,在打印到控制檯時無限遞歸。當代碼運行時,控制檯上不顯示任何內容。當我將SIGTERM
發送到程序時,它會打印硬編碼的「概念驗證」draw
輸出,但不會輸出無限遞歸(go
函數)。爲什麼我的Haskell程序從不打印到控制檯?
我懷疑這與延遲評估有關,輸出到控制檯的代碼在go
函數中永遠不會被調用,但我不知道如何解決它。任何建議將不勝感激!
Haskell代碼:
import Data.Maybe (isJust, fromJust)
import System.Random
import System.Console.ANSI
import qualified System.Console.Terminal.Size as Term
data RainDrop a = RainDrop
{ row :: !a
, col :: !a
, count :: !a
} deriving (Read,Show)
main :: IO()
main = do
clearScreen
-- proof that draw works
c <- applyX 10 draw (return (RainDrop 0 2 10))
go [return (RainDrop 0 0 10)]
applyX :: Int -> (a -> a) -> a -> a
applyX 0 _ x = x
applyX n f x = applyX (n-1) f (f x)
go :: [IO (RainDrop Int)] -> IO()
go [] = return()
go (x:xs) = do
prng <- newStdGen
go $ map draw $ maybeAddToQueue prng (x:xs)
maybeAddToQueue :: RandomGen g => g -> [IO (RainDrop Int)] -> [IO (RainDrop Int)]
maybeAddToQueue _ [] = []
maybeAddToQueue prng (x:xs) =
let
(noNewDrop, gen0) = randomR (True,False) prng
in
if noNewDrop
then x:xs
else (
do
(colR,gen1) <- randomCol gen0
return $ RainDrop 0 colR $ fst $ randomLen gen1
):x:xs
randomCol :: RandomGen g => g -> IO (Int, g)
randomCol prng = do
w <- Term.size >>= (\x -> return . Term.width $ fromJust x)
return $ randomR (0,(w-1)) prng
randomLen :: RandomGen g => g -> (Int, g)
randomLen = randomR (4,32)
draw :: IO (RainDrop Int) -> IO (RainDrop Int)
draw rain = do
x <- rain
prng <- newStdGen
setCursorPosition (row x) (col x)
putChar . toGlyph $ fst $ randomR range prng
return (RainDrop (succ $ row x) (col x) (count x))
toGlyph x
| isJust a = fromJust a
| otherwise = x
where a = lookup x dictionary
dictionary =
let (a,b) = range
in zip [a..b] encoding
encoding =
let (a,b) = splitAt 16 katakana
(c,d) = splitAt 7 b
in a ++ numbers ++ C++ ['A'..'Z'] ++ d
range = (' ','~')
katakana = ['・'..'゚']
numbers = "012Ƹ߈Ƽ6ߖȣ9"
非常感謝您的設計見解。我主要是讓它工作。當*不*使用'threadDelay'減慢輸出時,它看起來很糟糕。當使用'threadDelay'時,輸出被阻塞,直到發送'SIGTERM'。 –
@awashburn也許你可以用'threadDelay'問題來打開另一個問題。 –
@DanielWagner如果我解決不了,我會的。 –