对于Scala,如何在第一个错误时停止,但获取已计算的值

对于Scala,如何在第一个错误时停止,但获取已计算的值,scala,functional-programming,Scala,Functional Programming,例如,假设我有一个函数 def foo(): Either[String, Int] = ??? 我想调用这个函数三次。如果所有值都正确,则我需要求和。如果我有一个左,我想得到错误和所有先前右值的总和(~此时停止计算) 我找到的唯一方法是: List(foo, foo, foo).foldLeft((None, 0)) { case ((Some(err), sum), _) => (Some(err), sum) case ((None, sum), fn) => f

例如,假设我有一个函数

def foo(): Either[String, Int] = ???
我想调用这个函数三次。如果所有值都
正确
,则我需要求和。如果我有一个
,我想得到错误和所有先前
值的总和(~此时停止计算)

我找到的唯一方法是:

List(foo, foo, foo).foldLeft((None, 0)) { 
  case ((Some(err), sum), _) =>  (Some(err), sum)
  case ((None, sum), fn) => fn() match {
    case Left(err) => (Some(err), sum)
    case Right(x) => (None, sum + x)
  }
}

是否有一些通用的函数式编程特性(例如cats或scalaz)可以做到这一点

与尾部递归函数的解决方案基本相同:

 def sumRights(allFoos: List[() => Either[String, Int]], accum: Int = 0): (Option[String], Int) = {
    allFoos match {
      case Nil => (None, accum)
      case head :: tail =>
        head() match {
          case Right(x) => sumRights(tail, accum + x)
          case Left(err) => (Some(err), accum)
        }
    }

另一个必要的解决方案是在第一个左边用
splitWhere
进行拆分,并将第一个输出列表的所有值相加。

我不确定猫的scalaz是否有专门用于此的内容,但下面是如何在vanilla scala中解决此问题:

  • 惰性流:

    Stream.continually(foo()).take(3) match {
      case s =>
        val err = s.collectFirst {
          case Left(err) => err
        }
        val sum = s.takeWhile(_.isRight)
          .flatMap(_.right.toOption)
          .sum
    
        (err, sum)
    }
    
  • 将被延迟计算并缓存

  • 尾部递归:

    def rec(sum: Int = 0, i: Int = 2): (Option[String], Int) =
      if (i < 0) (None, sum) else foo() match {
        case Left(e) => (Some(e), sum)
        case Right(s) => rec(s + sum, i - 1)
      }
    
    def rec(sum:Int=0,i:Int=2):(选项[String],Int)=
    如果(i<0)(无,求和)else foo()匹配{
    案例左(e)=>(部分(e),总和)
    案例右侧=>rec(s+sum,i-1)
    }
    

  • 使用
    Stream.span

    val (ints, rest) = Stream.continually(foo()).take(3).span(_.isRight)
    
    val sum = ints.map(_.right.get).sum
    val error = rest.headOption
    

    您的尾部递归不满足对
    foo
    :“~在这一点上停止计算”的延迟求值要求。确实!重写为接受函数列表而不是值列表。