简体   繁体   English

在州monad中使用put with >>

[英]Use of put with >> in the state monad

From Thinking Functionally with Haskell (pg 248): 与Haskell的功能思考 (第248页):

You can think of type State sa as being 您可以将State sa视为存在类型

 type State sa = s -> (a,s) 

... ...

 put :: s -> State s () get :: State ss state :: (s -> (a,s)) -> State sa 

... state can be defined using put and get : ... state可以使用putget来定义:

 state f = do {s <- get; let (a,s') = fs; put s'; return a} 

Which I believe can be rewritten this way: 我认为可以这样重写:

get >>= \s ->
    let (a,s') = fs in
        put s' >> return a

So what is the purpose of put s' , if >> throws away its return value? 那么put s'的目的是什么,如果>>抛弃它的返回值?

>> doesn't throw away everything from the first argument. >>不会丢掉第一个论点中的所有内容。 The definition for the state monad (ignoring the newtype ) 状态monad的定义(忽略newtype

a >> b = \s -> let (x, s') = a s in b s'

So the state form the first argument is used by >> , but the 'return value' (the x component of the result) is ignored. 因此,第一个参数的状态由>> ,但是'返回值'(结果的x分量)被忽略。 That's the point of the state monad --- to track changes to the state without the programmer having to explicitly take them into account. 这是状态monad的重点---跟踪状态的变化,而程序员不必明确地将它们考虑在内。

Apparently, this isn't explained properly by what the OP has been reading, so here's how you can derive the definition above. 显然,OP已经阅读的内容没有对此进行适当的解释,因此,您可以在此处推导出上述定义。 The definition of >> across all monads is >>所有monad的定义是

a >> b = a >>= \ _ -> b

The definition of >>= for the state monad (ignoring newtype s) is >>= for state monad(忽略newtype s)的定义是

a >>= f = \ s -> let (x, s') = a s in f x s'

Now, substituting the definition of >>= into the definition of >> above and simplifying, we get: 现在,将>>=的定义替换为上面的>>的定义并简化,我们得到:

a >> b = let f = \ _ -> b in \ s -> let (x, s') = a s in f x s'
    = \ s -> let (x, s') = a s in (\ _ -> b) x s'
    = \ s -> let (x, s') = a s in b s'

So what is the purpose of put s' , if >> throws away its return value? 那么put s'的目的是什么,如果>>抛弃它的返回值?

(>>) does throw away the return value, but we don't use put for the return value. (>>)会丢弃返回值,但我们不使用put作为返回值。 The type of put is: put的类型是:

put :: s -> State s ()

The return value is of put is a () , and () is for the most part just an uninteresting placeholder. put的返回值是a () ,而()在很大程度上只是一个无趣的占位符。 The meaningful part of what put does -- replacing the state -- is not reflected in the return value. 什么样的有意义的部分put更换状态- -确实没有反映在返回值。 A similar case is the type of putStrLn :: String -> IO () . 类似的情况是putStrLn :: String -> IO ()

State is an example where the monad abstraction is used to encapsulate an effect . State是一个使用monad抽象来封装效果的示例。 In such cases, it's perfectly normal to have operations in which the effect of the operation is important, but it might not have a meaningful return value. 在这种情况下,操作的效果很重要,但它可能没有有意义的返回值,这是完全正常的。

I'll illustrate with an example. 我将以一个例子来说明。 Consider everyone's favorite recursive algorithm, the Fibonacci sequence: 考虑每个人最喜欢的递归算法,Fibonacci序列:

fib :: Int -> Int
fib 1 = 0
fib 2 = 1
fib n = fib (n-1) + fib (n-2)

We all know this is a pretty inefficient way to calculate these numbers, but how inefficient is it? 我们都知道这是计算这些数字的一种效率很低的方法,但效率如何呢? If we were using a lesser language, we would probably be tempted to hack in a mutable variable and increment it every time fib is called. 如果我们使用较小的语言,我们可能会想要破解一个可变变量并在每次调用fib递增它。 We can do something similar in Haskell in a purely functional way using State . 我们可以使用State以纯函数方式在Haskell中执行类似的操作。

Let's define a new version of fib: 让我们定义一个新版本的fib:

fib' :: Int -> State Int Int
fib' 1 = modify (+1) >> return 0
fib' 2 = modify (+1) >> return 1
fib' n = modify (+1) >> (+) <$> fib' (n-1) <*> fib' (n-2)

Now we can simultaneously compute the nth number and also count how many calls were made: 现在我们可以同时计算第n个数字,并计算调用的次数:

> runState (fib' 7) 0
(8,25)
> runState (fib' 10) 0
(34,109)
> runState (fib' 30) 0  -- this takes about 5 seconds on my machine
(514229,1664079)

Ok, that's great but how does it answer the question? 好的,那很好但是它如何回答这个问题呢? The point to note in the above implementation is modify (+1) . 上述实现中要注意的是modify (+1) This has the effect of adding 1 to the counter, but doesn't have any useful result on its own. 这具有向计数器添加1的效果,但是它本身没有任何有用的结果。 We use >> to sequence it with the next operation, which does have a useful result, namely the computation. 我们使用>>对下一个操作进行排序,它有一个有用的结果,即计算。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM