如果任一函数返回Left,则中断循环

时间:2018-04-13 22:30:27

标签: scala monads future either

在下面的代码中,我需要的是,如果either1either2返回Left,则停止处理循环,如果发生这种情况,那么mainFunction也必须返回Left。此外,either1.Lefteither2.Left返回的字符串需要由mainFunction.Left返回。如何使这项工作?

def either1 (i:Int): Future[Either[String,Int]] = Future {
                    if (i<3)
                       Right(i*2)
                    else
                       Left("error 1")
}

def either2 (i:Int): Future[Either[String,Int]] = Future {
                    if (i>3)
                       Right(i*2)
                    else
                       Left("error 2")
}


val seq = Seq ( 1,1,2,2,3,4,5 )

def mainFunction: Future[Either[String,Int]] = Future {

     val seq2 = seq.map { number =>
             if (number % 2 == 0)
                  either1(number) // <-- this needs to break the loop if it returns Left
             else
                  either2(number) // <-- this needs to break the loop if it returns Left
        }

    Right(seq2.length)  // <-- seq2 is a sequence of Futures
}

3 个答案:

答案 0 :(得分:1)

在Scala中,标准集合不提供方法。 您可以使用scala.util.control.Breaks或者您必须编写 递归,类似这样的

val seq = Seq(1, 1, 2, 2, 3, 4, 5)

def either1(i: Int): Either[String, Int] = {
    if (i < 3) Right(i * 2)
    else Left("error 1")
}

def either2(i: Int): Either[String, Int] = {
    if (i > 3) Right(i * 2)
    else Left("error 2")
}

def rec(seq: Seq[Int], acc: Seq[Either[String, Int]]): Seq[Either[String, Int]] = seq match {
    case Nil => acc
    case x :: xs =>
        val xx = if (x % 2 == 0) either1(x) else either2(x)
        xx match {
            case Left(_) => acc
            case Right(value) => rec(xs, acc :+ Right(value))
        }
    }

rec(seq, Seq())

答案 1 :(得分:1)

下面的代码一直在迭代序列,直到遇到第一个错误,然后返回错误消息,或者返回固定数字42(这就是“无关紧要的东西” - 要求)。 / p>

import scala.concurrent._
import scala.util._
import scala.concurrent.ExecutionContext.Implicits.global

def either1(i: Int): Future[Either[String,Int]] = Future {
  if (i < 3) Right(i * 2)
  else Left("error 1")
}

def either2 (i:Int): Future[Either[String,Int]] = Future {
  if (i > 3) Right(i * 2)
  else Left("error 2")
}

val seq = Seq(1, 1, 2, 2, 3, 4, 5)
val doesntMatter = 42

/** Returns either first error message returned by `either1` or
  * `either2`, or the fixed number `doesntMatter`.
  */
def mainFunction: Future[Either[String, Int]] = {
  def recHelper(remaining: List[Int]): Future[Either[String, Int]] = {
    remaining match {
      case Nil => Future { Right(doesntMatter) }
      case h :: t => (if (h % 2 == 0) either1(h) else either2(h)).flatMap {
        headEither =>
        headEither match {
          case Left(s) => Future { Left(s) }
          case Right(n) => recHelper(t)
        }
      }
    }
  }
  recHelper(seq.toList)
}

val res = mainFunction
Thread.sleep(2000)
println(res) // Future(Success(Left(error 2)))

如果您经常这样做的次数多一次,请考虑查看Scala Cats' EitherT,以及专门针对所有monadic类型的此类用例定义的方法tailRecM

答案 2 :(得分:0)

如果库函数可以执行我想要的操作,我通常会避免使用递归函数。

在这种情况下,我们可以使用takeWhile来获取Right的所有主要元素。但是,map调用仍会处理Seq的每个元素,因此您需要使用view来评估这个懒惰:

val seq2 = seq.view.map { number =>
   if (number % 2 == 0)
     either1(number)
   else
     either2(number)
 }.takeWhile(_.isRight)

您仍然遇到问题,即either函数实际上会返回Future,因此在LeftRight完成之前无法对其进行测试。

相关问题