Scala 扁平鳞片试验
有没有一种简单的方法可以将try的集合展平,从而获得成功的try值,或者仅仅是失败的try值? 例如:Scala 扁平鳞片试验,scala,try-catch,scala-2.10,Scala,Try Catch,Scala 2.10,有没有一种简单的方法可以将try的集合展平,从而获得成功的try值,或者仅仅是失败的try值? 例如: def map(l:List[Int]) = l map { case 4 => Failure(new Exception("failed")) case i => Success(i) } val l1 = List(1,2,3,4,5,6) val result1 = something(map(l1)) result1: Failure(Exception("f
def map(l:List[Int]) = l map {
case 4 => Failure(new Exception("failed"))
case i => Success(i)
}
val l1 = List(1,2,3,4,5,6)
val result1 = something(map(l1))
result1: Failure(Exception("failed"))
val l2 = List(1,2,3,5,6)
val result2 = something(map(l2))
result2: Try(List(1,2,3,5,6))
您如何处理集合中的多个故障?可能没有您希望的那么简单,但这是可行的:
def flatten[T](xs: Seq[Try[T]]): Try[Seq[T]] = {
val (ss: Seq[Success[T]]@unchecked, fs: Seq[Failure[T]]@unchecked) =
xs.partition(_.isSuccess)
if (fs.isEmpty) Success(ss map (_.get))
else Failure[Seq[T]](fs(0).exception) // Only keep the first failure
}
val xs = List(1,2,3,4,5,6)
val ys = List(1,2,3,5,6)
println(flatten(map(xs))) // Failure(java.lang.Exception: failed)
println(flatten(map(ys))) // Success(List(1, 2, 3, 5, 6))
请注意,分区
的使用并不像它得到的那样类型安全,正如@unchecked
注释所证明的那样。在这方面,一个foldLeft
组合两个序列Seq[Success[T]]
和Seq[Failure[T]]
会更好
如果要保留所有故障,可以使用以下方法:
def flatten2[T](xs: Seq[Try[T]]): Either[Seq[T], Seq[Throwable]] = {
val (ss: Seq[Success[T]]@unchecked, fs: Seq[Failure[T]]@unchecked) =
xs.partition(_.isSuccess)
if (fs.isEmpty) Left(ss map (_.get))
else Right(fs map (_.exception))
}
val zs = List(1,4,2,3,4,5,6)
println(flatten2(map(xs))) // Right(List(java.lang.Exception: failed))
println(flatten2(map(ys))) // Left(List(1, 2, 3, 5, 6))
println(flatten2(map(zs))) // Right(List(java.lang.Exception: failed,
// java.lang.Exception: failed))
少一点冗长,多一些类型安全:
def sequence[T](xs : Seq[Try[T]]) : Try[Seq[T]] = (Try(Seq[T]()) /: xs) {
(a, b) => a flatMap (c => b map (d => c :+ d))
}
结果:
sequence(l1)
res8:scala.util.Try[Seq[Int]]=失败(java.lang.Exception:失败)
res9:scala.util.Try[Seq[Int]]=Success(列表(1,2,3,5,6))
看一下升降箱monad。借助于
tryo
constructor函数,它精确地为您提供了所需的抽象
使用tryo
可以将功能提升到框中
。然后,该框要么包含函数的结果,要么包含错误。然后,您可以使用常用的一元辅助函数(flatMap、filter等)访问该框,而不必担心该框是否包含错误或函数的结果
例如:
import net.liftweb.util.Helpers.tryo
List("1", "2", "not_a_number") map (x => tryo(x.toInt)) map (_ map (_ + 1 ))
结果
List[net.liftweb.common.Box[Int]] =
List(
Full(2),
Full(3),
Failure(For input string: "not_a_number",Full(java.lang.NumberFormatException: For input string: "not_a_number"),Empty)
)
您可以使用flatMap
List("1", "2", "not_a_number") map (x => tryo(x.toInt)) flatMap (_ map (_ + 1 ))
结果
List[Int] = List(2, 3)
还有多种其他辅助方法,例如用于组合框(链接错误消息时)。您可以在这里找到一个很好的概述:
您可以单独使用,无需使用整个升降框架。对于上述示例,我使用了以下sbt脚本:
scalaVersion := "2.9.1"
libraryDependencies += "net.liftweb" %% "lift-common" % "2.5-RC2"
libraryDependencies += "net.liftweb" %% "lift-util" % "2.5-RC2"
作为对Impredicative回答和评论的补充,如果您的依赖项中同时包含和/scala210:
> scala210/console
[warn] Credentials file /home/folone/.ivy2/.credentials does not exist
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.0 (OpenJDK 64-Bit Server VM, Java 1.7.0_17).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.util._
import scala.util._
scala> def map(l:List[Int]): List[Try[Int]] = l map {
| case 4 => Failure(new Exception("failed"))
| case i => Success(i)
| }
map: (l: List[Int])List[scala.util.Try[Int]]
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> import scalaz.contrib.std.utilTry._
import scalaz.contrib.std.utilTry._
scala> val l1 = List(1,2,3,4,5,6)
l1: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> map(l1).sequence
res2: scala.util.Try[List[Int]] = Failure(java.lang.Exception: failed)
scala> val l2 = List(1,2,3,5,6)
l2: List[Int] = List(1, 2, 3, 5, 6)
scala> map(l2).sequence
res3: scala.util.Try[List[Int]] = Success(List(1, 2, 3, 5, 6))
您需要scalaz为列表
(隐藏在MonadPlus
实例中)获取一个应用程序
,以获取方法。对于Try
的遍历
,您需要scalaz contrib,这是序列
的类型签名所必需的。
Try
在scalaz之外,因为它只出现在scala 2.10中,scalaz的目标是交叉编译到早期版本)。对于失败第一次操作来说,这几乎是最小的:
def something[A](xs: Seq[Try[A]]) =
Try(xs.map(_.get))
(在这一点上,您不应该费心创建一个方法;只需使用Try
)。如果你想要所有的失败,一个方法是合理的;我会使用或:
def something[A](xs: Seq[Try[A]]) =
Try(Right(xs.map(_.get))).
getOrElse(Left(xs.collect{ case Failure(t) => t }))
这些是我的礼物:
def sequence[A, M[_] <: TraversableOnce[_]](in: M[Try[A]])
(implicit cbf:CanBuildFrom[M[Try[A]], A, M[A]]): Try[M[A]] = {
in.foldLeft(Try(cbf(in))) {
(txs, tx) =>
for {
xs <- txs
x <- tx.asInstanceOf[Try[A]]
} yield {
xs += x
}
}.map(_.result())
}
def序列[A,M[;]
为了{
xs从Scala 2.13开始,大多数集合都提供了一种方法,该方法基于返回Right
或Left
的函数对元素进行分区
在本例中,我们可以使用一个函数调用partitionMap
,该函数将我们的Try
s转换为或s(),以便将Success
es划分为Right
s,将Failure
s划分为Left
s
然后,只需根据是否存在左撇子来匹配生成的左撇子和右撇子的分区元组:
tries.partitionMap(_.toEither) match {
case (Nil, rights) => Success(rights)
case (firstLeft :: _, _) => Failure(firstLeft)
}
// * val tries = List(Success(10), Success(20), Success(30))
// => Try[List[Int]] = Success(List(10, 20, 30))
// * val tries = List(Success(10), Success(20), Failure(new Exception("error1")))
// => Try[List[Int]] = Failure(java.lang.Exception: error1)
中间partitionMap
步骤的详细信息:
List(Success(10), Success(20), Failure(new Exception("error1"))).partitionMap(_.toEither)
// => (List[Throwable], List[Int]) = (List(java.lang.Exception: error1), List(10, 20))
请说出您的结果应该是什么样子。列表(1、2、3、异常、5、7)
?Scalaz会将此操作称为序列
,但它在标准库中丢失了。它看起来不错,尽管追加操作不会对性能造成很大影响吗?公平点-我忘了默认的Seq
是一个列表
。添加反向/使用向量由您决定!很好,我知道了dnt在/:
之前看到过foldLeft快捷方式很好!(尽管它会重新抛出异常,如果有)您将如何扩展它以保留所有异常?@mhs-如果故障不常见或性能不重要,您需要重新抛出,因为这是处理问题的最简单方法。异常的堆栈跟踪已经生成,因此抛出/捕获本身并不太昂贵。如果我想保留所有异常,我会打包它们n一个或者相反,例如在我的编辑中,+1“在这方面,一个foldLeft积累两个序列Seq[Success[T]]和Seq[Failure[T]]会更好。“是的!scala生态系统中有这么多验证或失败类型(选项,或者,Try,scalaz.Disjunction,scalaz.validation,…),但在公共方法和公共模式中,我仍然找不到任何这样的双序列折叠示例。
List(Success(10), Success(20), Failure(new Exception("error1"))).partitionMap(_.toEither)
// => (List[Throwable], List[Int]) = (List(java.lang.Exception: error1), List(10, 20))