扫描的常见模式()我最终不关心状态

时间:2015-03-09 06:27:00

标签: scala state fold

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

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)
  }
}

然而,现在我发现自己也在列表和矢量中这样做,所以我想知道我能做些更普遍的事情吗?

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在标准库中不可用,但如果您正在寻找名称或有关实施的想法,那么这是一个可以开始的地方。

相关问题