[英]Scala and State Monad
我一直在努力了解State Monad。 与其如何使用不同,尽管这并不总是很容易找到。 但是我发现的State Monad的每一次讨论基本上都有相同的信息,总有一些我不理解的东西。
以这篇文章为例。 其中作者有以下内容:
case class State[S, A](run: S => (A, S)) {
...
def flatMap[B](f: A => State[S, B]): State[S, B] =
State(s => {
val (a, t) = run(s)
f(a) run t
})
...
}
我可以看到类型正确排列。 但是,我根本不了解第二次run
。
也许我正在错误地看待这个monad的整个目的。 我从HaskellWiki得到的印象是,状态monad有点像状态机, run
允许转换(但是,在这种情况下,状态机并不像大多数状态机那样具有固定的状态转换)。 如果是这种情况,那么在上面的代码(a, t)
中将表示单个转换。 f
的应用表示对该值和State
的修改(生成新的State对象)。 这让我对第二次run
的全部内容感到困惑。 这似乎是第二次“过渡”。 但这对我没有任何意义。
我可以看到在生成的State
对象上调用run
会产生一个新的(A, S)
对,当然,这些对是排序的类型所必需的。 但我真的不明白这应该是做什么的。
那么,这里到底发生了什么? 在这里建模的概念是什么?
编辑:2015年12月22日
所以,看来我并没有很好地表达我的问题。 让我试一试。
在同一篇博客文章中,我们看到以下map
代码:
def map[B](f: A => B): State[S, B] =
State(s => {
val (a, t) = run(s)
(f(a), t)
})
显然,只有一个调用run
在这里。
我一直试图调和的模型是,对run
的调用会移动我们通过单个状态变化保持前进的状态。 这似乎是map
的情况。 但是,在flatMap
我们有两个run
调用。 如果我的模型是正确的,那将导致“跳过”状态变化。
要使用下面提供的示例@Filppo,第一次run
调用将导致返回(1, List(2,3,4,5))
,第二次调用将导致(2, List(3,4,5))
,有效地跳过第一个。 因为,在他的例子中,紧接着是对map
的调用,这将导致(Map(a->2, b->3), List(4,5))
。
显然,这不是正在发生的事情。 所以我的整个模型是不正确的。 推理这个的正确方法是什么?
第2编辑:2015年12月22日
我只是试着按照我在REPL中说的做。 而我的直觉是正确的让我更加困惑。
scala> val v = State(head[Int]).flatMap { a => State(head[Int]) }
v: State[List[Int],Int] = State(<function1>
scala> v.run(List(1,2,3,4,5))
res2: (Int, List[Int]) = (2,List(3, 4, 5))
因此, flatMap
这种实现确实会跳过一个状态。 然而,当我运行@Filippo的例子时,我得到了同样的答案。 这里到底发生了什么?
要理解“第二次运行”,让我们“向后”分析它。
签名def flatMap[B](f: A => State[S, B]): State[S, B]
表明我们需要运行函数f
并返回其结果。
要执行函数f
我们需要给它一个A
我们从哪里得到一个?
好吧,我们已经run
,可以给我们A
S
,所以我们需要一个S
因为我们这样做: s => val (a, t) = run(s) ...
我们把它读作“给定一个S
执行run
函数,它产生我们A
和一个新的S
这是我们的”第一次“运行。
现在我们有一个A
,我们可以执行f
。 这就是我们想要的, f(a)
给了我们一个新的State[S, B]
。 如果我们这样做,那么我们有一个函数,它接受S
并返回Stats[S, B]
:
(s: S) =>
val (a, t) = run(s)
f(a) //State[S, B]
但是函数S => State[S, B]
不是我们想要返回的! 我们想要返回State[S, B]
。
我们怎么做? 我们可以将此函数包装到State
:
State(s => ... f(a))
但它不起作用,因为State
取S => (B, S)
,而不是S => State[B, S]
。 所以我们需要得到(B, S)
State[B, S]
。
我们只需调用其run
方法并为其提供我们刚刚在上一步生成的状态即可! 这是我们的“第二次”运行。
因此,我们可以通过flatMap
执行以下转换:
s => // when a state is provided
val (a, t) = run(s) // produce an `A` and a new state value
val resState = f(a) // produce a new `State[S, B]`
resState.run(t) // return `(S, B)`
这给了我们S => (S, B)
,我们只用State
构造函数包装它。
查看这些“两次运行”的另一种方式是:
首先 - 我们用“我们的” run
函数转换状态
第二 - 我们将转换后的状态传递给函数f
并让它自己进行转换。
所以我们一个接一个地“链接”状态转换。 而这正是monad所做的:它们为我们提供了按顺序安排计算的能力。
state monad
归结为此功能从一个state
到另一个state
(加上A
):
type StatefulComputation[S, +A] = S => (A, S)
Tony在该博客中提到的实现“捕获”该函数进入case class
run
:
case class State[S, A](run: S => (A, S))
将state
bind
到另一个state
的flatmap
实现调用2个不同的run
:
// the `run` on the actual `state`
val (a: A, nextState: S) = run(s)
// the `run` on the bound `state`
f(a).run(nextState)
编辑 2 State
之间的flatmap
示例
考虑一个简单地调用.head
到List
获取A
的函数,以及.tail
用于下一个状态S
// stateful computation: `S => (A, S)` where `S` is `List[A]`
def head[A](xs: List[A]): (A, List[A]) = (xs.head, xs.tail)
2 State(head[Int])
简单绑定State(head[Int])
:
// flatmap example
val result = for {
a <- State(head[Int])
b <- State(head[Int])
} yield Map('a' -> a,
'b' -> b)
所述的预期行为for-comprehension
是“提取物”的列表到第一元件a
和第二个在b
。 结果状态S
将是运行列表的剩余尾部:
scala> result.run(List(1, 2, 3, 4, 5))
(Map(a -> 1, b -> 2),List(3, 4, 5))
怎么样? 调用在某些状态s
上run
的“有状态计算” head[Int]
:
s => run(s)
这给出了列表的head
( A
)和tail
( B
)。 现在我们需要将tail
传递给下一个State(head[Int])
f(a).run(t)
其中f
在flatmap
签名中:
def flatMap[B](f: A => State[S, B]): State[S, B]
也许为了更好地理解这个例子中的f
是什么,我们应该将for-comprehension
为:
val result = State(head[Int]).flatMap {
a => State(head[Int]).map {
b => Map('a' -> a, 'b' -> b)
}
}
用f(a)
我们传入a
函数,用run(t)
我们传递修改后的状态。
我接受了@ AlexyRaga对我的问题的回答。 我认为@ Filippo的回答非常好,事实上,给了我一些额外的思考。 感谢你们俩。
我认为我遇到的概念上的困难实际上主要与“ run
方法是什么意思”有关。 也就是说,它的目的和结果是什么。 我把它看作是一个“过渡”功能(从一个州到下一个州)。 而且,经过时尚之后,就是这样。 但是,它不会从给定( this
)状态转换到下一个状态。 相反,它采用初始State
并返回( this
)状态的值和新的“当前”状态(不是状态转换序列中的下一个状态)。
这就是flatMap
方法按flatMap
实现的原因。 当您生成一个新State
您需要基于传入的初始状态从中获取当前值/状态对,然后可以将其作为函数包装在新的State
对象中。 你并没有真正过渡到一个新的国家。 只需在新的State
对象中重新包装生成的状态。
我沉浸在传统的状态机中,看看这里发生了什么。
再次感谢大家。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.