scala中相邻项目的平均值

Average for adjacent items in scala

我有一个seq

val seq = Seq(1, 9, 5, 4, 3, 5, 5, 5, 8, 2)

我想得到每个相邻(左右)数字的平均值,这意味着在上面的例子中有以下计算: [(1+9)/2, (1+9+5)/3, (9+5+4)/3, (5+4+3)/3, (4+3+5)/3, (3+5+5)/3, (5+5+5)/3, (5+5+8)/3, (5+8+2)/3, (8+2)/2]

其他例子是:

Seq() shouldBe Seq()
Seq(3) shouldBe Seq(3.0d)
Seq(1, 4) shouldBe Seq(2.5d, 2.5d)
Seq(1, 9, 5, 4, 3, 5, 5, 5, 8, 2) shouldBe Seq(5.0, 5.0, 6.0, 4.0, 4.0, 13.0 / 3, 5.0, 6.0, 5.0, 5.0)

我能够得到:numbers.sliding(2, 1).map(nums => nums.sum.toDouble / nums.length).toSeq。但是它不考虑之前的值。
我试着用 foldLeft 来做 - 它也很麻烦。

有没有简单的方法来做到这一点?我错过了什么?

老实说,我认为使用简单的 (虽然有点长) tail-recursive 算法更容易解决这类问题。

def adjacentAverage(data: List[Int]): List[Double] = {
  @annotation.tailrec
  def loop(remaining: List[Int], acc: List[Double], previous: Int): List[Double] =
    remaining match {
      case x :: y :: xs =>
        loop(
          remaining = y :: xs,
          ((previous + x + y).toDouble / 3.0d) :: acc,
          previous = x
        )
  
      case x :: Nil =>
        (((previous + x).toDouble / 2.0d) :: acc).reverse
    }

  data match {
    case x :: y :: xs => loop(remaining = y :: xs, acc = ((x + y).toDouble / 2.0d) :: Nil, previous = x)
    case x :: Nil     => x.toDouble :: Nil
    case Nil          => Nil
  }
}

可以看到运行here.

如果您想要不同大小的滑动 window 怎么办,例如 4 或 7 或...?挑战是获得 build-up、(1), (1,2), (1,2,3), (1,2,3,4), ... 和 tail-off、..., (6,7,8,9), (7,8,9), (8,9), (9)

def windowAvg(input: Seq[Int], windowSize: Int): Seq[Double] =
  if (input.isEmpty || windowSize < 1) Seq()
  else {
    val windows = input.sliding(windowSize).toSeq
    val buildUp = windows.head.inits.toSeq.tail.reverse.tail
    val tailOff = windows.last.tails.toSeq.tail.init
    (buildUp ++ windows ++ tailOff).map(x => x.sum.toDouble / x.length)
  }

如果您真的需要 trim 关闭结果中的开头和结尾 single-number 条目,那么我将把它留作 reader.[=13 的练习=]

我通过 foldLeft 的繁琐解决方案(没有火箭科学)

    def adjacentAverage(numbers: Seq[Int]): Seq[Double] = numbers.foldLeft(("x", Seq[Double](), 0)) {(acc, num) => acc._1 match {
      case "x" => if (numbers.isEmpty) ("x", Seq(), acc._3 + 1) else if (numbers.length == 1) ("x", Seq(num.toDouble), acc._3 + 1) else (num.toString, acc._2 :+ ((num.toDouble + numbers(acc._3 + 1).toDouble) / 2.0), acc._3 + 1)
      case _ => (num.toString, try {acc._2 :+ ((acc._1.toDouble + num.toDouble + numbers(acc._3 + 1).toDouble) / 3.0)} catch {case e: IndexOutOfBoundsException => acc._2 :+ ((acc._1.toDouble + num.toDouble) / 2.0) }, acc._3 + 1)
    }}._2