我发现自己经常做以下事情:
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)
}
}
然而,现在我发现自己也在列表和矢量中这样做,所以我想知道我能做些更普遍的事情吗?
答案 0 :(得分:5)
您描述的组合子(或至少非常相似的组合)通常称为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
在标准库中不可用,但如果您正在寻找名称或有关实施的想法,那么这是一个可以开始的地方。