【问题标题】:How can I write this simple code using the state monad?如何使用 state monad 编写这个简单的代码?
【发布时间】:2016-10-01 14:24:28
【问题描述】:

我是 Haskell 的初学者,我遇到了一个我想使用 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。

我理解 state monad 的目的(或者至少我认为我理解),并且我希望它允许我重新编写它,以便我最终得到类似的东西

runProgram :: ???????
runProgram = do
    put 0
    addToState 6
    subtractFromState 4
    value <- getValue
    return value

但是,尽管我阅读了所有教程,但我仍然不太清楚如何将我的代码转换为这种形式。

当然,我的实际机器的状态要复杂得多,而且我也在传递它的输出(将传递给另一台机器)和其他各种东西,所以我非常热衷于简化它。知道如何为这个简化的示例执行此操作将非常有帮助。

更新:在 Lee 的出色回答之后,我现在知道如何做到这一点,但是当我有多个交互机器时,我被困在如何以同样优雅的形式编写代码。我在a new question 中问过这个问题。

【问题讨论】:

    标签: haskell monads state-monad


    【解决方案1】:

    首先,您需要将现有函数转换为返回 State Machine 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
            (Register i) <- get
            pure i
    

    然后你可以将它们组合成一个有状态的计算:

    program :: State Machine Int
    program = do
      addToState 6
      subtractFromState 4
      getValue
    

    最后,您需要使用evalState 运行此计算以获得最终结果并丢弃状态:

    runProgram :: Int
    runProgram = evalState program (Register 0)
    

    【讨论】:

    • 非常感谢,这是无价之宝。我必须进行一些更改才能使其运行(我根本没有抱怨,这样做很有启发性)。我不确定是将它们编辑到您的答案中还是保持原样 - 如果您有偏好,请告诉我。
    • @Nathaniel - 欢迎您进行任何您想要的编辑。
    • 如果您有兴趣进一步提供帮助,我有一个follow on question,关于当我有多个交互的有状态对象时如何以这种风格进行编程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多