Scala 将多个转换链接到状态Monad
我开始使用state monad来清理我的代码。我已经让它解决了我的问题,我处理了一个名为CDR的事务,并相应地修改了状态。 使用此函数执行状态更新,对于单个事务来说,它工作得非常好Scala 将多个转换链接到状态Monad,scala,scalaz,scala-cats,state-monad,scalaz7,Scala,Scalaz,Scala Cats,State Monad,Scalaz7,我开始使用state monad来清理我的代码。我已经让它解决了我的问题,我处理了一个名为CDR的事务,并相应地修改了状态。 使用此函数执行状态更新,对于单个事务来说,它工作得非常好 def addTraffic(cdr: CDR): Network => Network = ... 以下是一个例子: scala> val processed: (CDR) => State[Network, Long] = cdr => | for { | m <
def addTraffic(cdr: CDR): Network => Network = ...
以下是一个例子:
scala> val processed: (CDR) => State[Network, Long] = cdr =>
| for {
| m <- init
| _ <- modify(Network.addTraffic(cdr))
| p <- get
| } yield p.count
processed: CDR => scalaz.State[Network,Long] = $$Lambda$4372/1833836780@1258d5c0
scala> val r = processed(("122","celda 1", 3))
r: scalaz.State[Network,Long] = scalaz.IndexedStateT$$anon$13@4cc4bdde
scala> r.run(Network.empty)
res56: scalaz.Id.Id[(Network, Long)] = (Network(Map(122 -> (1,0.0)),Map(celda 1 -> (1,0.0)),Map(1 -> Map(1 -> 3)),1,true),1)
我尝试对此进行调整失败,但我无法使它们类型的签名匹配,因为我的状态函数需要cdr(事务)输入
谢谢TL;DR您可以在
CDR
s的集合(例如List
)上的traverse
类型类中使用带有以下签名的函数:CDR=>State[Network,Long]
。结果将是一个状态[网络,列表[长]]
。或者,如果您不关心那里的列表[Long]
,您可以使用遍历
,它将返回状态[Network,Unit]
。最后,如果您想“聚合”结果T
,并且T
形成Monoid
,您可以使用Foldable
中的foldMap
,它将返回状态[Network,T]
,其中T
是链中所有T
的组合(例如折叠)结果
代码示例现在提供更多详细信息和代码示例。我将使用Scalaz而不是Scalaz来回答这个问题,因为我从未使用过后者,但概念是相同的,如果您仍然有问题,我将找出正确的语法 假设我们要处理以下数据类型和导入:
import cats.implicits._
import cats.data.State
case class Position(x : Int = 0, y : Int = 0)
sealed trait Move extends Product
case object Up extends Move
case object Down extends Move
case object Left extends Move
case object Right extends Move
很明显,位置
表示二维平面中的一个点,移动
可以上下左右移动该点
现在,让我们创建一个方法,让我们能够看到在给定时间我们所处的位置:
def whereAmI : State[Position, String] = State.inspect{ s => s.toString }
以及一种改变我们位置的方法,给定一个移动
:
def move(m : Move) : State[Position, String] = State{ s =>
m match {
case Up => (s.copy(y = s.y + 1), "Up!")
case Down => (s.copy(y = s.y - 1), "Down!")
case Left => (s.copy(x = s.x - 1), "Left!")
case Right => (s.copy(x = s.x + 1), "Right!")
}
}
请注意,这将返回一个字符串
,移动名称后跟感叹号。这只是为了模拟从Move
到其他对象的类型更改,并显示如何聚合结果。更多关于这一点在一点
现在让我们尝试一下我们的方法:
val positions : State[Position, List[String]] = for{
pos1 <- whereAmI
_ <- move(Up)
_ <- move(Right)
_ <- move(Up)
pos2 <- whereAmI
_ <- move(Left)
_ <- move(Left)
pos3 <- whereAmI
} yield List(pos1,pos2,pos3)
(您可以忽略那里的.value
,这是一个怪癖,因为状态[s,a]
实际上只是状态[Eval,s,a]
的别名)
如您所见,这与您预期的一样,您可以创建不同的“蓝图”(例如,状态修改序列),一旦提供初始状态,就会应用这些蓝图
现在,为了真正回答您的问题,假设我们有一个列表[Move]
,我们希望将它们依次应用到初始状态,并得到结果:我们使用遍历
或者,如果您根本不需要A
(在您的案例中是列表
),您可以使用遍历
,而不是遍历
,结果类型为:
val result_ : State[Position, List[String]] = moves.traverse_(move)
result_.run(Position()).value // (Position(-1,-1),Unit)
最后,如果您的A
在状态中键入[S,A]
形成Monoid
,那么您也可以使用foldMap
fromFoldable
组合(例如折叠)所有A
的计算结果。一个简单的例子(可能没用,因为这只会连接所有String
s)如下:
val result : State[Position,String] = moves.foldMap(move)
result.run(Position()).value // (Position(-1,-1),Down!Down!Left!Up!)
最后一种方法对您是否有用,实际上取决于您拥有的A
,以及将其组合起来是否合理
这应该是您在场景中所需要的全部 只是为了检查我是否正确理解了您的问题:您想知道如何将此模式依次应用于(比如)5个CDR吗?e、 g.给定一个初始状态,您希望将第一个CDR应用于该状态,将第二个CDR应用于上一步的结果状态,依此类推……正确吗?是的,但对于迭代器上的CDR,我可以手动应用它们,就像手动一个接一个地应用一样,但我的目标是能够针对任何大小的块运行此操作。感谢您的回复,这让我走上了正确的道路。我不得不做一些小的调整;首先定义了一个别名,因为State有太多的“漏洞”:键入NetworkState[A]=State[Network,A],然后我创建了我的CDR列表,以提供val miscdrs:list[CDR]=(new-CDRFromRandom(5).buffer map{x=>x.getCDR})。toList val结果:NetworkState[list[Long]=CDRs.traverse[NetworkState,Long](processCDR)我也必须给编译器一些帮助。。。val result=cdrs.traverse[NetworkState,Long](processCDR)result.run(Network.empty)我现在还有两个问题:第一,是否可以使用迭代器来保持它的惰性?第二,我对列表不太感兴趣,因为它可能会变得非常大(数亿个元素,我可以去掉它并保留最后一个值吗?感谢您回答第一个问题:您不需要
迭代器来保持它的惰性。State[S,A]
是StateT[Eval,S,A]的别名
,而Eval
所做的恰恰是:它使它变得懒惰。如果你不需要整个列表
,那么你可以使用foldMap
fromFoldable
而不是traverse
。这个概念很相似,但给出了一个List[Move]
和一个函数Move=>State[Position,String]
,它将返回状态[位置,字符串]
,其中最后的字符串是字符串的幺半群的合并
操作的结果,在这种情况下(这是串联,但您可以将其更改为案例中需要的).最后,如果这个回答有用,请给出答案。
val moves = List(Down, Down, Left, Up)
val result : State[Position, List[String]] = moves.traverse(move)
result.run(Position()).value // (Position(-1,-1),List(Down!, Down!, Left!, Up!))
val result_ : State[Position, List[String]] = moves.traverse_(move)
result_.run(Position()).value // (Position(-1,-1),Unit)
val result : State[Position,String] = moves.foldMap(move)
result.run(Position()).value // (Position(-1,-1),Down!Down!Left!Up!)