gpt4 book ai didi

Scalaz 状态 monad 示例

转载 作者:行者123 更新时间:2023-12-03 05:18:29 25 4
gpt4 key购买 nike

我还没有看到很多 scalaz 状态 monad 的例子。有this example,但很难理解,而且堆栈溢出似乎只有一个other question

我将发布一些我玩过的示例,但我欢迎其他示例。另外,如果有人可以提供为什么使用 initmodifyputgets 的示例,那就太好了.

编辑:here 是一个关于状态 monad 的精彩的 2 小时演示。

最佳答案

我假设 scalaz 7.0.x 和以下导入(查看 scalaz 6.x 的答案历史记录):

import scalaz._
import Scalaz._

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

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

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

// 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, State[S, B]]],而不是 Function[A, B]。使用State函数...

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

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

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() 状态计算填充列表。

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

使用序列获取State[Random, List[(Int,Int)]]。我们可以提供类型别名。

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 来推断类型:

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 计算抛出的总和并计算频率。

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 应用于 tenDoubleThrowstraverse 相当于 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来推断类型:

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类型。

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

有关 @ziggystar 评论的其他信息

我放弃了尝试使用stateT,也许其他人可以展示是否可以增强StateFreqStateRandom来执行组合计算。我发现两个状态转换器的组成可以这样组合:

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 是一个单参数函数,获取第一个状态转换器的结果并返回一个状态转换器。然后以下内容就可以工作:

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 示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7734756/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com