如何使用状态monad编写这个简单的代码? [英] How can I write this simple code using the state monad?

查看:94
本文介绍了如何使用状态monad编写这个简单的代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Haskell的初学者,我遇到了一个我想使用state monad的情况。 (或者至少,我认为我就是这么想的)。国家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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆