如何修复递归recHelper方法中Future[Either]结果无法拼接的问题?
搞定递归处理Future[Either]的问题!
嘿,我明白你现在的困扰——要递归处理列表里的元素,每个元素返回一个Future[Either],得等所有都成功才返回装着结果列表的Right,只要有一个失败就立刻返回对应的Left对吧?我给你捋清楚实现思路,直接上能用的代码!
核心思路
咱们用尾递归+Future异步处理的组合来做:
- 用一个累加器(acc)来逐步收集成功的结果,避免递归栈溢出
- 每次处理列表的第一个元素,拿到异步结果后判断:
- 要是
Left,直接终止递归返回这个错误 - 要是
Right,把结果加到累加器里,继续处理剩下的元素
- 要是
- 最后把累加器反转一下,恢复原列表的顺序
完整实现代码
import scala.concurrent.Future 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) // 带累加器的尾递归辅助方法 private def recHelper(remaining: List[Int], acc: List[Int]): Future[Either[String, List[Int]]] = remaining match { // 列表处理完了,把累加器反转后包成Right返回 case Nil => Future.successful(Right(acc.reverse)) // 处理当前元素 case head :: tail => // 这里默认用either1,要是你需要交替用either1/either2,后面我给你加调整方案 either1(head).flatMap { // 遇到错误直接返回,终止递归 case Left(err) => Future.successful(Left(err)) // 成功的话把结果加入累加器,继续递归处理剩下的元素 case Right(res) => recHelper(tail, res :: acc) } } // 对外调用的方法,初始累加器为空列表 def processSequence(seq: Seq[Int]): Future[Either[String, List[Int]]] = recHelper(seq.toList, Nil)
代码细节解释
- 尾递归:把累加器作为参数传递,每次递归都是最后一步操作,不会栈溢出
- 异步处理:用
flatMap来衔接Future的异步结果,保证顺序处理(要是你想并行处理可以用Future.sequence,但那样会等所有结果出来才判断失败,而递归是遇到失败立刻终止,看你需求) - 结果顺序:因为递归时新结果是加到列表头部的,最后用
reverse把顺序改回原列表的顺序
扩展:交替调用either1和either2
要是你需要对列表里的元素交替调用either1和either2,可以给每个元素加个标记,修改递归逻辑:
private def recHelperAlternating(remaining: List[(Int, Boolean)], acc: List[Int]): Future[Either[String, List[Int]]] = remaining match { case Nil => Future.successful(Right(acc.reverse)) case (head, useEither1) :: tail => // 根据标记选择调用哪个方法 val targetFuture = if (useEither1) either1(head) else either2(head) targetFuture.flatMap { case Left(err) => Future.successful(Left(err)) case Right(res) => recHelperAlternating(tail, res :: acc) } } // 调用前给每个元素打标记:偶数索引用either1,奇数用either2 val seqWithFlag = seq.zipWithIndex.map { case (num, idx) => (num, idx % 2 == 0) }.toList def processAlternating(seq: Seq[Int]): Future[Either[String, List[Int]]] = recHelperAlternating(seqWithFlag, Nil)
测试一下
- 调用
processSequence(seq),因为所有元素都满足i<3,会返回Future(Right(List(2, 2, 4, 4))) - 要是把seq改成
Seq(3, 1),会立刻返回Future(Left("error 1")),不会继续处理后面的元素
这样就能完美实现你要的逻辑啦!
内容的提问来源于stack exchange,提问作者ps0604
相关产品推荐
相关产品推荐

