You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以单子式方式从列表元素的Either中获取首个Right结果?

解决方案:使用Scala的findMap(2.13+)结合收尾处理

你要找的内置函数是Scala 2.13及以上版本中IterableOps提供的findMap方法,它能完美实现短路遍历、返回首个成功结果的需求,再配合简单的收尾逻辑就能满足"全失败返回最后一个Left"的要求。

核心逻辑说明

  • findMap会遍历集合中的元素,对每个元素应用传入的函数:
    • 一旦函数返回Some(b)(可通过Either.toOption将Right(b)转成Some(b)),就立即停止遍历,返回该Some(b)
    • 如果所有元素处理后都返回None,则返回None
  • 若要在全失败时返回最后一个Left,只需额外用foldLeft收集最后一次失败的错误信息即可。

适配你的测试代码示例

def tester(i: Int): Either[String, Int] = {
  if (i % 2 == 0) Right(100 / (4 - i)) else Left(i.toString)
}

val items = 1 to 5

// 先获取首个成功的Right值,短路遍历
val firstSuccess = items.findMap(i => tester(i).toOption)

// 处理全失败的情况,取最后一个Left的错误
val finalResult: Either[String, Int] = firstSuccess match {
  case Some(value) => Right(value)
  case None =>
    val lastError = items.foldLeft("") { (acc, i) =>
      tester(i).left.getOrElse(acc)
    }
    Left(lastError)
}

测试这个代码:

  • 遍历到i=2时,tester(2)返回Right(50),findMap会立即停止,不会处理i=3/4/5,完全符合你"不触发后续副作用"的要求
  • 如果所有元素都返回Left,finalResult会拿到最后一个元素的错误信息

替代思路(针对旧版本Scala)

如果你使用Scala 2.12及以下版本,没有findMap,可以用collectFirst配合PartialFunction,但要注意避免重复调用处理函数:

val firstSuccess = items.view.map(i => i -> tester(i)).collectFirst {
  case (_, Right(value)) => value
}
// 后续全失败处理逻辑同上

这里用view实现惰性求值,确保每个元素只被处理一次,找到首个Right就停止。

内容的提问来源于stack exchange,提问作者Edward Peters

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 06:45:36