我想练习IO
在 Haskell 中使用 monad,所以我决定制作一个“屏幕保护程序”程序,它会在打印到控制台时无限递归。当代码运行时,控制台上不会出现任何内容。当我将它发送SIGTERM
到程序时,它会打印硬编码的“概念证明”draw
输出,但没有来自无限递归(go
函数)的输出。
我怀疑这与惰性评估有关,go
永远不会调用函数中输出到控制台的代码,但我不知道如何修复它。任何建议将不胜感激!
哈斯克尔代码:
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"