我正在从“LYAH”学习 Haskell,并学习了 State monad。作为练习,我正在努力实现一个简单的“虚拟CPU”。状态单子似乎很适合这个,但我不知道如何使用它。这是我目前所拥有的一个非常精简的示例(没有状态单子):
data Instruction = Incr | Decr | Halt
data Computer = Computer { program :: [Instruction],
acc :: Int,
pc :: Int,
halted :: Bool
}
main = do
let comp = Computer { program = [Incr, Decr, Incr, Incr, Halt]
, acc = 0
, pc = 0
, halted = False
}
execute comp
execute :: Computer -> IO ()
execute comp = do
let (output, comp') = step comp
putStrLn output
case halted comp' of
False -> execute comp'
True -> return ()
step :: Computer -> (String, Computer)
step comp = let inst = program comp !! pc comp
in case inst of
Decr -> let comp' = comp{ acc = acc comp - 1,
pc = pc comp + 1 }
output = "Increment:" ++
" A = " ++ (show $ acc comp') ++
" PC = " ++ (show $ pc comp')
in (output, comp')
Incr -> let comp' = comp{ acc = acc comp + 1,
pc = pc comp + 1 }
output = "Decrement:" ++
" A = " ++ (show $ acc comp') ++
" PC = " ++ (show $ pc comp')
in (output, comp')
Halt -> let comp' = comp{halted = True}
output = "HALT"
in (output, comp')
Run Code Online (Sandbox Code Playgroud)
我的step函数看起来像状态单子,但我不知道如何在这里使用它。会适用吗?它会简化这段代码,还是这个例子更好?
绝对地。step可以非常机械地翻译:
import Control.Monad.Trans.State\n\nstep :: State Computer String\nstep = do\n comp <- get\n case program comp !! pc comp of\n Decr -> do \n let comp\' = comp { acc = acc comp - 1\n , pc = pc comp + 1 }\n put comp\'\n return $ "Increment:"\n ++ " A = " ++ (show $ acc comp\')\n ++ " PC = " ++ (show $ pc comp\')\n Incr -> do\n let comp\' = comp { acc = acc comp + 1\n , pc = pc comp + 1 }\n put comp\'\n return $ "Decrement:"\n ++ " A = " ++ (show $ acc comp\')\n ++ " PC = " ++ (show $ pc comp\')\n Halt -> do\n put $ comp{halted = True}\n return "HALT"\nRun Code Online (Sandbox Code Playgroud)\n(这些let comp\' = ...仍然有点尴尬。通过使用库中的状态字段修饰符可以使其变得更加命令lens式)
您可能会注意到,这State实际上只是StateT,我使用的函数都不是专门针对此的。即你可以立即将签名概括为
step :: Monad m => StateT Computer m String\nRun Code Online (Sandbox Code Playgroud)\n事实证明这对 很有用execute,因为你可以将状态修改与IO副作用 \xe2\x80\x93 散布在一起,这正是变压器的用途。
import Control.Monad.IO.Class\n\nexecute :: StateT Computer IO ()\nexecute = do\n output <- step\n liftIO $ putStrLn output\n comp <- get\n case halted comp of\n False -> execute\n True -> return ()\nRun Code Online (Sandbox Code Playgroud)\n最后,在main你只需要
main = do\n let comp = ...\n evalStatT execute comp\nRun Code Online (Sandbox Code Playgroud)\n