Haskell 与>&燃气轮机;在单子状态下

Haskell 与>&燃气轮机;在单子状态下,haskell,state-monad,Haskell,State Monad,从Haskell的功能性思考(第248页): 您可以将类型状态s a视为 type State s a = s -> (a,s) put :: s -> State s () get :: State s s state :: (s -> (a,s)) -> State s a 状态可以使用put和get定义: state f = do {s <- get; let (a,s') = f s; put s'; return a}

从Haskell的功能性思考(第248页):

您可以将类型
状态s a
视为

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

put :: s -> State s ()
get :: State s s
state :: (s -> (a,s)) -> State s a
<代码>状态可以使用
put
get
定义:

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

如果
>
丢弃了它的返回值,那么
放置s'
的目的是什么呢?

>
不会丢弃第一个参数的所有内容。状态monad的定义(忽略
newtype

因此,第一个参数的状态形式由
>
使用,但“返回值”(结果的
x
组件)被忽略。这就是状态单子的要点——跟踪状态的变化,而程序员不必显式地考虑它们

显然,OP阅读的内容并不能很好地解释这一点,所以下面是如何推导出上面的定义。所有单子的
>
定义如下

a >> b = a >>= \ _ -> b
状态monad(忽略
newtype
s)的
>=
定义为

现在,将上述
>>=
的定义替换为
>
的定义并简化,我们得到:

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'
那么,如果
>
丢弃了它的返回值,那么
放置s'
的目的是什么呢

(>>)
会丢弃返回值,但我们不会使用
put
作为返回值。
put
的类型为:

put :: s -> State s ()

返回值是
put
()
,而
()
在大多数情况下只是一个无趣的占位符。
put
所做的有意义的部分——替换状态——没有反映在返回值中。类似的情况是
putStrLn::String->IO()

状态的类型
是使用monad抽象来封装效果的示例。在这种情况下,操作的效果很重要是完全正常的,但它可能没有有意义的返回值

我将用一个例子来说明。考虑每个人最喜欢的递归算法,Fibonacci序列:

fib :: Int -> Int
fib 1 = 0
fib 2 = 1
fib n = fib (n-1) + fib (n-2)
我们都知道这是一种非常低效的计算这些数字的方法,但它的效率有多低?如果我们使用的是较少的语言,我们可能会尝试在每次调用
fib
时插入一个可变变量并增加它。我们可以使用
State
以纯函数的方式在Haskell中做类似的事情

让我们定义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)

好的,那很好,但是它如何回答这个问题呢?上述实现中需要注意的一点是
modify(+1)
。这有向计数器添加1的效果,但其本身没有任何有用的结果。我们使用
>
将其与下一个操作进行排序,这确实有一个有用的结果,即计算。

一个类似的问题可能(也可能没有!)很有启发性:在
main=putStrLn“hello world!”>main中
putStrLn“hello world!”
的目的是什么,如果
>
扔掉它的返回值?我现在看到短语“如果>>扔掉它的返回值?”分散了我问题的意图,这不是问“这个操作符(通常)有什么意义”,而是“它在状态monad的上下文中如何运行?”@jcast的回答是我想的。在哪里(源代码除外)我会找到这个定义吗?我经常看到的文档中不包含>>@planarian源代码,通常是在你找到定义的地方。在别处看到它们是个例外,而不是规则。这让我感到惊讶。我一直认为Hoogle是寻找它们的地方。@planarian Hoogle会给你一个类型声明和链接文档;通常有一个到源代码的链接。在
(>>)
(和其他类方法)的特定情况下,当然,您希望实例的源代码,而不是
(>>)
声明的源代码。啊,“docs”=“Haddock”.是的,我不会依赖这些。我建议你找一些长格式的教程,至少要考虑一下。(对不起,我现在没有一个好的推荐。)。
put :: s -> State s ()
fib :: Int -> Int
fib 1 = 0
fib 2 = 1
fib n = fib (n-1) + fib (n-2)
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)
> 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)