gpt4 book ai didi

scala - scan() 的常见模式,我最终不关心状态

转载 作者:行者123 更新时间:2023-12-01 10:32:59 28 4
gpt4 key购买 nike

我发现自己经常做如下事情:

val adjustedActions = actions.scanLeft((1.0, null: CorpAction)){
case ((runningSplitAdj, _), action) => action match {
case Dividend(date, amount) =>
(runningSplitAdj, Dividend(date, amount * runningSplitAdj))
case s @ Split(date, sharesForOne) =>
((runningSplitAdj * sharesForOne), s)
}
}
.drop(1).map(_._2)

在本例中,我需要累加 runningSplitAdj,以便更正操作列表中的红利。在这里,我使用 scan 来维护我需要的状态,以便更正操作,但最后,我只需要操作。因此,我需要为状态中的初始操作使用 null,但最后,删除该项目并映射所有状态。

是否有更优雅的方式来构建它们?在 RxScala Observables 的上下文中,我实际上创建了一个新的运算符来执行此操作(在 RxJava 邮件列表的帮助下):

implicit class ScanMappingObs[X](val obs: Observable[X]) extends AnyVal {
def scanMap[S,Y](f: (X,S) => (Y,S), s0: S): Observable[Y] = {
val y0: Y = null.asInstanceOf[Y]
// drop(1) because scan also emits initial state
obs.scan((y0, s0)){case ((y, s), x) => f(x, s)}.drop(1).map(_._1)
}
}

但是,现在我发现自己也在对 Lists 和 Vectors 这样做,所以我想知道我是否可以做一些更通用的事情?

最佳答案

您描述的组合器(或至少非常相似的东西)通常称为 mapAccum。采用以下简化的 scanLeft 用法:

val xs = (1 to 10).toList

val result1 = xs.scanLeft((1, 0.0)) {
case ((acc, _), i) => (acc + i, i.toDouble / acc)
}.tail.map(_._2)

这等同于以下内容(使用 Scalaz's implementation of mapAccumLeft ):

xs.mapAccumLeft[Double, Int](1, {
case (acc, i) => (acc + i, i.toDouble / acc)
})._2

mapAccumLeft 返回一对最终状态和每一步的结果序列,但它不需要您指定虚假的初始结果(将被忽略然后丢弃),并且您不必映射整个集合来摆脱状态——您只需取对中的第二个成员。

遗憾的是 mapAccumLeft 在标准库中不可用,但如果您正在寻找一个名称或有关实现的想法,这是一个开始的地方。

关于scala - scan() 的常见模式,我最终不关心状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28936280/

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