如果我有Seq
,我可以map
覆盖它。
val ss = Seq("1", "2", "3")
println(ss.map(s => s.toInt)) // List(1, 2, 3)
但有时,传递给map
的函数可能会失败。
val ss = Seq("1", "2", "c")
println(ss.map(s => try { Success(s.toInt) } catch { case e: Throwable => Failure(e) })) // List(Success(1), Success(2), Failure(java.lang.NumberFormatException: For input string: "c"))
最后一个将返回Seq[Try[Int]]
。我真正想要的是Try[Seq[Int]]
,如果任何一个映射是Failure
,它将停止迭代并返回Failure
。如果没有错误,我希望它只返回所有已转换的元素,从Try
解压缩。
Scala的惯用方法是什么?
答案 0 :(得分:4)
你可能会过度思考这个问题。您map
中的匿名函数与Try.apply
基本相同。如果您希望以Try[Seq[Int]]
结尾,那么您可以将Seq
包裹在Try.apply
和map
内:
scala> val ss = Try(Seq("1", "2", "c").map(_.toInt))
ss: scala.util.Try[Seq[Int]] = Failure(java.lang.NumberFormatException: For input string: "c")
如果toInt
中的任何一个失败,它将抛出异常并停止执行,并成为Failure
。
答案 1 :(得分:1)
不确定它是否具有惯用性,但我会做这样的事情:
import util.{Try, Success, Failure}
import collection.mutable.ListBuffer
def toInt(s: String) =
// Correct usage would be Try(s.toInt)
try {
Success(s.toInt)
}
catch {
case e: Throwable => Failure(e)
}
def convert[A](ss: Seq[String], f: String => Try[A]) = {
ss.foldLeft(Try(ListBuffer[A]())) {
case (a, s) =>
for {
xs <- a
x <- f(s)
}
yield xs :+ x
}.map(_.toSeq)
}
scala> convert(List("1", "2"), toInt)
scala.util.Try[Seq[Int]] = Success(List(1, 2))
scala> convert(List("1", "c"), toInt)
scala.util.Try[Seq[Int]] = Failure(java.lang.NumberFormatException: For input string: "c")
如果你真的想提前退出而不是跳过元素,你可以使用旧的递归:
def convert[A](ss: Seq[String], f: String => Try[A]) = {
@annotation.tailrec
def loop(ss: Seq[String], acc: ListBuffer[A]): Try[Seq[A]] = {
ss match {
case h::t =>
f(h) match {
case Success(x) => loop(t, acc :+ x)
case Failure(e) => Failure(e)
}
case Nil =>
Success(acc.toSeq)
}
}
loop(ss, ListBuffer[A]())
}