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

exp*_*ite 3 state scala 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)
Run Code Online (Sandbox Code Playgroud)

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,现在我发现自己也在对Lists和Vectors进行操作,所以我想知道我能做些更普遍的事情吗?

Tra*_*own 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)
Run Code Online (Sandbox Code Playgroud)

这相当于以下(使用Scalaz的实现mapAccumLeft):

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

mapAccumLeft 在每一步返回一对最终状态和一系列结果,但它不需要你指定一个虚假的初始结果(只会被忽略然后丢弃),你不必映射整个集合摆脱了状态 - 你只需要成对的第二个成员.

遗憾的mapAccumLeft是,标准库中没有这种功能,但如果您正在寻找名称或有关实施的想法,那么这是一个可以开始的地方.