Scalaz 状态 monad 示例 [英] Scalaz state monad examples

查看:36
本文介绍了Scalaz 状态 monad 示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我还没有看到很多 scalaz 状态 monad 的例子.有 这个示例但很难理解,堆栈上只有一个其他问题好像溢出了.

I haven't seen many examples of the scalaz state monad. There is this example but it is hard to understand and there is only one other question on stack overflow it seems.

我将发布一些我玩过的例子,但我欢迎更多的例子.另外,如果有人可以提供关于为什么使用 initmodifyputgets 的例子,那将会很棒.

I'm going to post a few examples I've played with but I would welcome additional ones. Also if somebody can provide example on why init, modify, put and gets are used for that would be great.

这里是关于状态 monad 的精彩 2 小时演示.

here is an awesome 2 hours presentation on the state monad.

推荐答案

我假设 scalaz 7.0.x 和以下导入(查看 scalaz 6.x 的回答历史em>):

I assume, scalaz 7.0.x and the following imports (look at answer history for scalaz 6.x):

import scalaz._
import Scalaz._

状态类型定义为 State[S, A] 其中 S 是状态的类型,A 是状态的类型被装饰的价值.创建状态值的基本语法使用了 State[S, A] 函数:

The state type is defined as State[S, A] where S is type of the state and A is the type of the value being decorated. The basic syntax to create a state value makes use of the State[S, A] function:

// Create a state computation incrementing the state and returning the "str" value
val s = State[Int, String](i => (i + 1, "str")) 

在初始值上运行状态计算:

To run the state computation on a initial value:

// start with state of 1, pass it to s
s.eval(1)
// returns result value "str"

// same but only retrieve the state
s.exec(1)
// 2

// get both state and value
s(1) // or s.run(1)
// (2, "str")

状态可以通过函数调用进行线程化.要做到这一点而不是 Function[A, B],定义 Function[A, State[S, B]]].使用 State 函数...

The state can be threaded through function calls. To do this instead of Function[A, B], define Function[A, State[S, B]]]. Use the State function...

import java.util.Random
def dice() = State[Random, Int](r => (r, r.nextInt(6) + 1))

然后可以使用 for/yield 语法来组合函数:

Then the for/yield syntax can be used to compose functions:

def TwoDice() = for {
  r1 <- dice()
  r2 <- dice()
} yield (r1, r2)

// start with a known seed 
TwoDice().eval(new Random(1L))
// resulting value is (Int, Int) = (4,5)

这是另一个例子.使用 TwoDice() 状态计算填充列表.

Here is another example. Fill a list with TwoDice() state computations.

val list = List.fill(10)(TwoDice())
// List[scalaz.IndexedStateT[scalaz.Id.Id,Random,Random,(Int, Int)]]

使用序列得到一个State[Random, List[(Int,Int)]].我们可以提供一个类型别名.

Use sequence to get a State[Random, List[(Int,Int)]]. We can provide a type alias.

type StateRandom[x] = State[Random,x]
val list2 = list.sequence[StateRandom, (Int,Int)]
// list2: StateRandom[List[(Int, Int)]] = ...
// run this computation starting with state new Random(1L)
val tenDoubleThrows2 = list2.eval(new Random(1L))
// tenDoubleThrows2  : scalaz.Id.Id[List[(Int, Int)]] =
//   List((4,5), (2,4), (3,5), (3,5), (5,5), (2,2), (2,4), (1,5), (3,1), (1,6))

或者我们可以使用 sequenceU 来推断类型:

Or we can use sequenceU which will infer the types:

val list3 = list.sequenceU
val tenDoubleThrows3 = list3.eval(new Random(1L))
// tenDoubleThrows3  : scalaz.Id.Id[List[(Int, Int)]] = 
//   List((4,5), (2,4), (3,5), (3,5), (5,5), (2,2), (2,4), (1,5), (3,1), (1,6))

另一个示例,使用 State[Map[Int, Int], Int] 来计算上面列表中总和的频率.freqSum 计算抛出和计数频率的总和.

Another example with State[Map[Int, Int], Int] to compute frequency of sums on the list above. freqSum computes the sum of the throws and counts frequencies.

def freqSum(dice: (Int, Int)) = State[Map[Int,Int], Int]{ freq =>
  val s = dice._1 + dice._2
  val tuple = s -> (freq.getOrElse(s, 0) + 1)
  (freq + tuple, s)
}

现在使用 traverse 将 freqSum 应用于 tenDoubleThrows.traverse 等价于 map(freqSum).sequence.

Now use traverse to apply freqSum over tenDoubleThrows. traverse is equivalent to map(freqSum).sequence.

type StateFreq[x] = State[Map[Int,Int],x]
// only get the state
tenDoubleThrows2.copoint.traverse[StateFreq, Int](freqSum).exec(Map[Int,Int]())
// Map(10 -> 1, 6 -> 3, 9 -> 1, 7 -> 1, 8 -> 2, 4 -> 2) : scalaz.Id.Id[Map[Int,Int]]

或者更简洁地使用 traverseU 来推断类型:

Or more succinctly by using traverseU to infer the types:

tenDoubleThrows2.copoint.traverseU(freqSum).exec(Map[Int,Int]())
// Map(10 -> 1, 6 -> 3, 9 -> 1, 7 -> 1, 8 -> 2, 4 -> 2) : scalaz.Id.Id[Map[Int,Int]]

请注意,因为 State[S, A]StateT[Id, S, A] 的类型别名,tenDoubleThrows2 最终被输入为 Id.我使用 copoint 将其转回 List 类型.

Note that because State[S, A] is a type alias for StateT[Id, S, A], tenDoubleThrows2 ends up being typed as Id. I use copoint to turn it back into a List type.

简而言之,似乎使用状态的关键是让函数返回一个修改状态的函数和所需的实际结果值... 免责声明:我从来没有使用过 state生产代码,只是想感受一下.

In short, it seems the key to use state is to have functions returning a function modifying the state and the actual result value desired... Disclaimer: I have never used state in production code, just trying to get a feel for it.

关于@ziggystar 评论的附加信息

我放弃了尝试使用 stateT 可能是其他人可以展示是否可以增加 StateFreqStateRandom 以执行组合计算.相反,我发现两个状态转换器的组合可以像这样组合:

I gave up on trying using stateT may be someone else can show if StateFreq or StateRandom can be augmented to perform the combined computation. What I found instead is that the composition of the two state transformers can be combined like this:

def stateBicompose[S, T, A, B](
      f: State[S, A],
      g: (A) => State[T, B]) = State[(S,T), B]{ case (s, t) =>
  val (newS, a) = f(s)
  val (newT, b) = g(a) apply t
  (newS, newT) -> b
}

它基于 g 是一个单参数函数,它获取第一个状态转换器的结果并返回一个状态转换器.那么以下将起作用:

It's predicated on g being a one parameter function taking the result of the first state transformer and returning a state transformer. Then the following would work:

def diceAndFreqSum = stateBicompose(TwoDice, freqSum)
type St2[x] = State[(Random, Map[Int,Int]), x]
List.fill(10)(diceAndFreqSum).sequence[St2, Int].exec((new Random(1L), Map[Int,Int]()))

这篇关于Scalaz 状态 monad 示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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