Делать вещи неизменными при работе с циклами в Scala - PullRequest
1 голос
/ 22 апреля 2020

Я написал несколько строк кода на Scala, но не знаю, как заставить то же самое работать с неизменяемыми переменными (val). Любая помощь будет высоко ценится.

class Test {

  def process(input: Iterable[(Double, Int)]): (Double, Int) = {
    var maxPrice: Double = 0.0
    var maxVolume: Int = 0

    for ((price, volume) <- input) {
      if (price > maxPrice) {
        maxPrice = price
      }
      if (volume > maxVolume) {
        maxVolume = volume
      }
    }

    (maxPrice, maxVolume)
  }
}

Может кто-нибудь помочь мне преобразовать все VAR в VAL и сделать его более функциональным? Заранее спасибо! :)

Ответы [ 2 ]

7 голосов
/ 22 апреля 2020

Использование .foldLeft:

def process(input: Iterable[(Double, Int)]): (Double, Int) =
  input.foldLeft(0.0 -> 0) { case ((maxPrice, maxVolume), (price, volume)) =>
    val newPrice = if (maxPrice < price) price else maxPrice
    val newVolume = if (maxVolume < volume) volume else maxVolume
    newPrice -> newVolume
  }
2 голосов
/ 22 апреля 2020

Для сравнения приведено хвостовое рекурсивное решение

  def process(input: Iterable[(Double, Int)]): (Double, Int) = {
    @tailrec def loop(remaining: Iterable[(Double, Int)], maxPrice: Double, maxVolume: Int): (Double, Int) = {
      remaining match {
        case Nil => maxPrice -> maxVolume
        case (price, volume) :: tail =>
          val newPrice = if (maxPrice < price) price else maxPrice
          val newVolume = if (maxVolume < volume) volume else maxVolume
          loop(tail, newPrice, newVolume)
      }
    }
    loop(input, 0, 0)
  }

и соответствующий эталонный тест jmh

@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.Throughput))
class So61366933 {
  def mario(input: Iterable[(Double, Int)]): (Double, Int) = {
    @tailrec def loop(remaining: Iterable[(Double, Int)], maxPrice: Double, maxVolume: Int): (Double, Int) = {
      remaining match {
        case Nil => maxPrice -> maxVolume
        case (price, volume) :: tail =>
          val newPrice = if (maxPrice < price) price else maxPrice
          val newVolume = if (maxVolume < volume) volume else maxVolume
          loop(tail, newPrice, newVolume)
      }
    }
    loop(input, 0, 0)
  }


  def mateusz(input: Iterable[(Double, Int)]): (Double, Int) =
    input.foldLeft(0.0 -> 0) { case ((maxPrice, maxVolume), (price, volume)) =>
      val newPrice = if (maxPrice < price) price else maxPrice
      val newVolume = if (maxVolume < volume) volume else maxVolume
      newPrice -> newVolume
    }

  import scala.util.Random._
  def arbTuple: (Double, Int) = nextDouble() -> nextInt()
  val input = List.fill(1000)(arbTuple)

  @Benchmark def foldLeft = mateusz(input)
  @Benchmark def tailRec = mario(input)
}

, где

sbt "jmh:run -i 5 -wi 5 -f 2 -t 1 bench.So61366933"

вывод

[info] Benchmark             Mode  Cnt       Score      Error  Units
[info] So61366933.foldLeft  thrpt   10   80999.752 ± 2118.095  ops/s
[info] So61366933.tailRec   thrpt   10  259875.842 ± 7718.674  ops/s
...