如何使用状态monad编写这个简单的代码? [英] How can I write this simple code using the state monad?
问题描述
下面是我目前代码的一个非常简单的版本。正如你所看到的,我是通过函数对线程状态进行线程化的,我的问题就是如何使用 do
表示法重写代码,这样我就不会有
data Machine = Register Int
addToState :: Machine - > Int - > Machine
addToState(Register s)a = Register $ s + a
subtractFromState :: Machine - > Int - > Machine
subtractFromState(Register s)a = Register(s-a)
getValue :: Machine - > Int
getValue(Register s)= s
initialState = Register 0
runProgram = getValue(subtractFromState(addToState initialState 6)4)
该代码模拟一个简单的抽象机器,它具有单个寄存器,以及添加到寄存器的指令,从中减去并获取它的价值。结束时的程序将寄存器初始化为0,加上6,减去4并返回结果,当然是2。 (或者至少我认为我是这样做的),并且我期望它能让我重写这个,这样我就能得到像
runProgram :: ???????
然而,尽管我读过的所有教程仍然不知道如何将代码转换为这种形式。
runProgram = do
put 0
addToState 6
subtractFromState 4
value< - getValue
返回值
当然,我的实际机器的状态要复杂得多,而且我还将其输出(将传递给另一台机器)和其他各种东西传递出去,所以我很想简化它。知道如何做这个简化的例子将是一个非常大的帮助。
更新:在李的伟大答案后,我现在知道该怎么做这一点,但我坚持如何在具有多个交互机器时以相同的优雅形式编写代码。我在一个新问题上询问了这个问题。
$首先,您需要将现有函数转换为返回状态机a
值:import Control.Monad.State.Lazy
data Machine = Register Int
addToState :: Int - > State Machine()
addToState i = do
(Register x)< - get
put $ Register(x + i)
subtractFromState :: Int - > ; State Machine()
subtractFromState i = do
(Register x)< - get
put $ Register(x - i)
getValue :: State Machine Int
getValue = do
(注册i)< - 获得
纯粹
$ b $然后你可以把它们组合成一个有状态的计算:
pre $ program $ State
program = do
addToState 6
subtractFromState 4
getValue
终于你需要可以用 evalState
运行这个计算来获得最终结果并放弃状态:
runProgram :: Int
runProgram = evalState program(Register 0)
I'm a beginner at Haskell and I've come across a situation where I would like to use the state monad. (Or at least, I think I that's what I'd like to use.) There are a million tutorials for the state monad, but all of them seem to assume that my main goal is to understand it on a deep conceptual level, and consequently they stop just before the part where they say how to actually develop software with it. So I'm looking for help with a simplified practical example.
Below is a very simple version of what my current code looks like. As you can see, I'm threading state through my functions, and my question is simply how to re-write the code using the do
notation so that I won't have to do that.
data Machine = Register Int
addToState :: Machine -> Int -> Machine
addToState (Register s) a = Register $ s+a
subtractFromState :: Machine -> Int -> Machine
subtractFromState (Register s) a = Register (s-a)
getValue :: Machine -> Int
getValue (Register s) = s
initialState = Register 0
runProgram = getValue (subtractFromState (addToState initialState 6) 4)
The code simulates a simple abstract machine that has a single register, and instructions to add to the register, subtract from it, and get its value. The "program" at the end initialises the register to 0, adds 6 to it, subtracts 4 and returns the result, which of course is 2.
I understand the purpose of the state monad (or at least think I do), and I expect that it will allow me to re-write this so that I end up with something like
runProgram :: ???????
runProgram = do
put 0
addToState 6
subtractFromState 4
value <- getValue
return value
However, despite all the tutorials I've read I still don't quite know how to transform my code into this form.
Of course, my actual machine's state is much more complicated, and I'm also passing around its output (which will be passed to another machine) and various other things, so I'm quite keen to simplify it. Knowing how to do it for this simplified example would be a very great help.
Update: after Lee's great answer I now know how to do this, but I'm stuck on how to write code in the same elegant form when I have multiple interacting machines. I've asked about that in a new question.
First you need to convert your existing functions to return State Machine a
values:
import Control.Monad.State.Lazy
data Machine = Register Int
addToState :: Int -> State Machine ()
addToState i = do
(Register x) <- get
put $ Register (x + i)
subtractFromState :: Int -> State Machine ()
subtractFromState i = do
(Register x) <- get
put $ Register (x - i)
getValue :: State Machine Int
getValue = do
(Register i) <- get
pure i
then you can combine them into a stateful computation:
program :: State Machine Int
program = do
addToState 6
subtractFromState 4
getValue
finally you need can run this computation with evalState
to get the final result and discard the state:
runProgram :: Int
runProgram = evalState program (Register 0)
这篇关于如何使用状态monad编写这个简单的代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!