scala - 如何在 Scala 中最好地实现 "first success"(即,从一系列容易失败的操作中返回第一个成功)

标签 scala

根据标题,我知道有几种合理和惯用的方法可以返回第一次成功的计算,尽管我最感兴趣的是当我们想知道最后一次尝试的具体失败时如何处理这种情况所有尝试都失败了。作为第一次尝试,我们可以使用 collectFirst并执行以下操作:

  def main(args: Array[String]) {
    val xs = (1 to 5)
    def check(i: Int): Try[Int] = {
      println(s"checking: $i")
      Try(if (i < 3) throw new RuntimeException(s"small: $i") else i)
    }
    val z = xs.collectFirst { i => check(i) match { case s @ Success(x) => s } }
    println(s"final val: $z")
  }

如果我们不关心失败,这似乎是一个合理的解决方案(实际上,因为我们总是返回成功,所以我们从不返回 Failure ,只有在没有成功计算的情况下才返回 None )。

另一方面,为了处理所有尝试都失败的情况,我们可以使用以下方法捕获最后一次失败:
  def main2(args: Array[String]) {
    val xs = (1 to 5)
    def check(i: Int): Try[Int] = {
      println(s"checking: $i")
      Try(if (i < 3) throw new RuntimeException(s"small: $i") else i)
    }
    val empty: Try[Int] = Failure(new RuntimeException("empty"))
    val z = xs.foldLeft(empty)((e, i) => e.recoverWith { case _ => check(i) })
    println(s"final val: $z")
  }

这里的缺点是你创建了一个“假”Throwable表示空,如果列表很长,我们迭代整个列表,即使我们可能很早就成功了,即使后来的迭代本质上是空操作。

有没有更好的实现方式main2这是惯用的,不会受到上述缺点的影响?

最佳答案

你可以这样做:

  @tailrec
  def collectFirstOrFailure[T](l: List[T], f: T => Try[T]): Try[T] = {
    l match {
      case h :: Nil =>  f(h)
      case h :: t =>  // f(h) orElse collectFirstOrFailure(t, f) //wish I could do this but not tailrec approved!
        val res = f(h)
        if (res.isFailure){
          collectFirstOrFailure(t, f)
        }
        else {
          res
        }
      case Nil => Failure(new RuntimeException("empty"))
    }
  }

  val y = collectFirstOrFailure(xs.toList, check)
  println(s"final val: $y")

这不是很漂亮,我们仍然需要处理空列表的情况,但我们不会创建一个新的 Failure(new RuntimeException("empty"))每次运行(除非它是一个空列表),如果成功,我们就会停下来。我觉得 scalaz 有一些更好的方法来做到这一点,但我现在无法弄清楚。返回最后一个失败要求使这有点复杂。

更新

总有iterator ...
  def collectFirstOrFailureI[T](i: Iterator[T], f: T => Try[T]): Try[T] = {
    while (i.hasNext){
      val res = f(i.next())
      if (res.isSuccess || !i.hasNext){
        return res
      }
    }
    Failure(new RuntimeException("empty"))
  }

  xs.toIterator

  val x = collectFirstOrFailureI(xs.iterator, check)
  println(s"final val: $x")

关于scala - 如何在 Scala 中最好地实现 "first success"(即,从一系列容易失败的操作中返回第一个成功),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24399126/

相关文章:

azure - 如何使用scala并从Azure blob存储读取文件?

scala - Akka 流重试重复结果

scala 22 参数和更多参数 *

java - 将 Epoch 值转换为 12 小时格式的时间戳

scala - sbt - scala 项目 dockerization 中的问题

scala - scala 中的理解式半并行调用

scala - 在 Play 中排队异步任务! - 正确的方式

scala - 如何使用解析器组合器进行条件检查

scala - 将 Spark DataFrame 数据划分为单独的文件

scala - 零参数的抽象类和没有参数的抽象类有什么区别?