与前一个元素折叠

Fold with previous element

给定 val as: Seq[Int] = ...

很多时候我需要对两个连续的元素应用一个操作,例如

顺便说一下我不喜欢

for (i <- 1 until as.size) {
  // do something with as(i) and as(i - 1)
}

或由另一个

as.tail.foldLeft((0, as.head)) { (acc, e) =>
  // do something with acc._2 and e 
  // and try to not forget returning (_, e) 
}

如何为这种情况编写更好的代码?

您可以 zip 序列 as 与它自己的 tail:

for ((prev, curr) <- as zip as.tail) {
  // do something with `prev` and `curr`
}

或者您可以使用 sliding:

for (window <- as.sliding(2)) {
  val prev = window(0)
  val curr = window(1)
  // do something with `prev` and `curr`
}

这是为每个后续元素提供序列头部的一种方法。

val sq:Seq[Int] = Seq(. . .)

sq.headOption.fold(sq){hd =>
  sq.tail.map(/*map() or fold() with the hd value*/)
}

请注意,这对于包含 1 个或零个元素的集合是安全的。

可以自己制作支持前一个元素的折叠。 包含 1 个或零个元素的集合是安全的。

  def foldLeftWithPrevious[A, B](as: Seq[A], accumulator: B)(f: (B, A, A) => B): B = {
    @scala.annotation.tailrec
    def foldLeftInner(list2: Seq[A], previous: A, accumulator: B, f: (B, A, A) => B): B = {
      if (list2.isEmpty) accumulator
      else foldLeftInner(list2.tail, list2.head, f(accumulator, previous, list2.head), f)
    }

    if (as.length <= 1) accumulator
    else foldLeftInner(as.tail, as.head, accumulator, f)
  }

随时使用此代码段对其进行测试。

val foldLeftTest = Seq(1)
  foldLeftWithPrevious(foldLeftTest, 0)((accum, previous, current) => {
    println("accum = " + accum)
    println("previous = " + previous)
    println("current = " + current)
    println("accum will be... " + accum + " + " + previous + " + " + current)
    println("which is... " + (accum + previous + current))
    accum + previous + current
  })