Разрыв цикла, если любая из функций возвращает Left

В следующем коде мне нужно остановить обработку цикла, если либо either1 или же either2 вернитесь налево, и если это произойдет, то mainFunction должен также вернуться влево. Кроме того, строка возвращается either1.Left или же either2.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 ответа

Решение

Приведенный ниже код повторяет последовательность до тех пор, пока не встретит первую ошибку и не вернет сообщение об ошибке или фиксированное число 42 (это требование "не имеет значения, что он возвращает").

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)))

Если вы делаете это значительно чаще, чем один раз, подумайте о том, чтобы взглянуть на EitherT, а также на метод Scala Cats. tailRecM определено специально для таких случаев использования на всех классах монадических типов.

В 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())

Я обычно избегаю рекурсивных функций, если библиотечная функция будет делать то, что я хочу.

В этом случае мы можем использовать takeWhile взять на себя все ведущие элементы, которые Right, Тем не менее map вызов будет по-прежнему обрабатывать каждый элемент Seq так что вам нужно использовать view оценивать это лениво

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

У вас все еще есть проблема, что ваш either функции фактически возвращают Future и, следовательно, не может быть проверен на Left или же Right пока они не завершены.

Другие вопросы по тегам