Scala中相邻项目的平均值

问题描述

我有一个seq

val seq = Seq(1,9,5,4,3,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,2) shouldBe Seq(5.0,5.0,6.0,4.0,13.0 / 3,5.0)

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

有没有简单的方法可以做到这一点?我想念什么?

解决方法

说实话,我认为使用简单的(尽管有点长)尾递归算法更容易解决这类问题。

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

,

如果您想要不同大小的滑动窗口,例如4或7或...,该怎么办?面临的挑战是如何积累(1),(1,2),2,3),3,4),...和尾随的...,(6,7,8,9),(7,(8,(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)
  }

如果您确实需要精简结果中的开头和结尾的单数条目,那么我将其留给读者练习。

,

我通过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