【问题标题】:Accumulate result until some condition is met in functional way累积结果,直到以功能方式满足某些条件
【发布时间】:2013-11-12 18:37:33
【问题描述】:

我在循环中有一些昂贵的计算,我需要找到计算产生的最大值,但如果它等于 LIMIT,我想停止计算并返回我的累加器。

很容易通过递归来完成:

val list: List[Int] = ???
val UpperBound = ???

def findMax(ls: List[Int], max: Int): Int = ls match {
  case h :: rest =>
    val v = expensiveComputation(h)
    if (v == UpperBound) v
    else findMax(rest, math.max(max, v))

  case _ => max
}

findMax(list, 0)

我的问题:这个行为模板是否有名称并反映在 scala 集合库中?

更新:Do something up to N times or until condition is met in Scala - 有一个有趣的想法(使用惰性并在末尾找到或存在)但它并不直接适用于我的特定情况或需要可变 var 来跟踪累加器。

【问题讨论】:

  • 如果性能是一个非常高的优先级,请考虑if (v == UpperBound) return v。这种非本地返回是通过抛出异常来实现的,如果你不能@tailrec 并且列表很长,它可能会更快。

标签: scala functional-programming


【解决方案1】:

我认为你的递归函数非常好,所以老实说我不会改变它,但这里有一种使用集合库的方法:

list.foldLeft(0) {
  case (max, next) =>
    if(max == UpperBound)
      max
    else
      math.max(expensiveComputation(next), max)
}

它将遍历整个列表,但在达到上限后将不会执行昂贵的计算。

更新

根据您的评论,我尝试根据 LinearSeqOptimized 的 foldLeft 实现稍微调整 foldLeft。

def foldLeftWithExit[A, B](list: Seq[A])(z: B)(exit: B => Boolean)(f: (B, A) => B): B = {
  var acc = z
  var remaining = list
  while (!remaining.isEmpty && !exit(acc)) {
    acc = f(acc, list.head)
    remaining = remaining.tail
  }
  acc
}

调用它:

foldLeftWithExit(list)(0)(UpperBound==){
  case (max, next) =>  math.max(expensiveComputation(next), max)
}

您可能会使用隐式省略list 的第一个参数。 希望这会有所帮助。

【讨论】:

  • 嗯,合理,而且相当不错,确实——在没有计算的情况下遍历一个集合是轻量级的(当然,如果它不是很大,那么最好尽早停止)。递归很好,但我发现我需要三个变体——最小值、最大值和最大值,并附加更多数据。当我试图找到一个可以提供更大灵活性的通用变体时,我发现了很多让想法变得不可行的问题(至少对我来说)。
  • 用 foldLeft 更新了我的答案,该 foldLeft 退出并且不会遍历整个列表。看看有没有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-31
  • 2013-12-28
  • 1970-01-01
  • 2018-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多