展平 Scala 尝试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15495678/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:03:59  来源:igfitidea点击:

Flatten Scala Try

scalatry-catchscala-2.10

提问by J Pullar

Is there a simple way to flatten a collection of try's to give either a success of the try values, or just the failure? For example:

有没有一种简单的方法来展平一组 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("failed"))

val l2 = List(1,2,3,5,6)
val result2 = something(map(l2)) 

result2: Try(List(1,2,3,5,6))

And can how would you handle multiple Failures in the collection?

您将如何处理集合中的多个失败?

采纳答案by Malte Schwerhoff

Maybe not as simple as you hoped for, but this works:

也许不像你希望的那么简单,但这有效:

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

Note that the use of partitionis not as type safe as it gets, as witnessed by the @uncheckedannotations. In that respect, a foldLeftthat accumulates two sequences Seq[Success[T]]and Seq[Failure[T]]would be better.

请注意,partition正如注释所证明的那样,使用 of并不像它所获得的那样类型安全@unchecked。在这方面,一个foldLeft是积累两个序列Seq[Success[T]]Seq[Failure[T]]效果会更好。

If you wanted to keep all failures, you can use this:

如果你想保留所有的失败,你可以使用这个:

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

回答by Rex Kerr

This is pretty close to minimal for fail-first operation:

对于失败优先操作,这非常接近最小:

def something[A](xs: Seq[Try[A]]) =
  Try(xs.map(_.get))

(to the point where you shouldn't bother creating a method; just use Try). If you want all the failures, a method is reasonable; I'd use an Either:

(到了您不应该费心创建方法的程度;只需使用Try)。如果你想要所有的失败,一个方法是合理的;我会使用Either

def something[A](xs: Seq[Try[A]]) =
  Try(Right(xs.map(_.get))).
  getOrElse(Left(xs.collect{ case Failure(t) => t }))

回答by Impredicative

A little less verbose, and more type safe:

不那么冗长,类型更安全:

def sequence[T](xs : Seq[Try[T]]) : Try[Seq[T]] = (Try(Seq[T]()) /: xs) {
    (a, b) => a flatMap (c => b map (d => c :+ d))
}

Results:

结果:

sequence(l1)

res8: scala.util.Try[Seq[Int]] = Failure(java.lang.Exception: failed)

res8: scala.util.Try[Seq[Int]] = Failure(java.lang.Exception: failed)

sequence(l2)

res9: scala.util.Try[Seq[Int]] = Success(List(1, 2, 3, 5, 6))

res9: scala.util.Try[Seq[Int]] = Success(List(1, 2, 3, 5, 6))

回答by folone

As an addition to Impredicative's answer and comment, if you have both scalaz-sevenand scalaz-contrib/scala210 in your dependencies:

作为 Impredicative 的回答和评论的补充,如果您的依赖项中同时具有scalaz-sevenscalaz-contrib/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))

You need scalaz to get an Applicativeinstancefor the List(hidden in the MonadPlusinstance), to get the sequencemethod. You need scalaz-contrib for the Traverseinstanceof Try, which is required by the sequence's type signature. Trylives outside of scalaz, since it only appeared in scala 2.10, and scalaz aims to cross-compile to earlier versions).

你需要scalaz获得一个Applicative实例List(藏在MonadPlus实例),以获得sequence方法。对于 的Traverse实例,您需要 scalaz-contrib Try,这是sequence的类型签名所必需的。 Try存在于 scalaz 之外,因为它只出现在 scala 2.10 中,并且 scalaz 旨在交叉编译到早期版本)。

回答by stefan.schwetschke

Have a look on the liftweb Box monad. With the help of the tryoconstructor function, it gives you exactly the abstraction you are looking for.

看看liftweb Box monad。在tryo构造函数的帮助下,它为您提供了您正在寻找的抽象。

With tryoyou can lift a function into a Box. The box then either contains the result from the function or it contains an error. You can then access the box with the usual monadic helper functions (flatMap, filter, etc.), without bothering if the box contains an error or the result form the function.

随着tryo您可以解除功能成Box。然后该框要么包含该函数的结果,要么包含一个错误。然后,您可以使用通常的 monadic 辅助函数(flatMap、filter 等)访问该框,而无需担心该框是否包含错误或函数的结果。

Example:

例子:

import net.liftweb.util.Helpers.tryo

List("1", "2", "not_a_number") map (x => tryo(x.toInt)) map (_ map (_ + 1 ))

Results to

结果到

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

You can skip the erroneous values with flatMap

您可以跳过错误的值 flatMap

List("1", "2", "not_a_number") map (x => tryo(x.toInt)) flatMap (_ map (_ + 1 ))

Results

结果

List[Int] = List(2, 3)

There are multiple other helper methods, e.g. for combining boxes (while chaining error messages). You can find a good overview here: Box Cheat Sheet for Lift

还有多种其他辅助方法,例如用于组合框(同时链接错误消息)。您可以在此处找到一个很好的概述:Box Cheat Sheet for Lift

You can use it on its own, no need to use the whole lift framework. For the examples above I used the follwing sbt script:

您可以单独使用它,无需使用整个升降机框架。对于上面的示例,我使用了以下 sbt 脚本:

scalaVersion := "2.9.1"

libraryDependencies += "net.liftweb" %% "lift-common" % "2.5-RC2"

libraryDependencies += "net.liftweb" %% "lift-util" % "2.5-RC2"

回答by Xavier Guihot

Starting Scala 2.13, most collections are provided with a partitionMapmethod which partitions elements based on a function which returns either Rightor Left.

首先Scala 2.13,大多数集合都提供了一种partitionMap方法,该方法根据返回Right或的函数对元素进行分区Left

In our case we can call partitionMapwith a function that transforms our Trys into Eithers (Try::toEither) in order to partition Successes as Rights and Failures as Lefts.

在我们的例子中,我们可以调用partitionMap一个将Trys 转换为Eithers ( Try::toEither)的函数,以便将Successes划分为Rights,将Failures划分为Lefts。

Then it's a simple matter of matching the resulting partitioned tuple of lefts and rights based on whether or not there are lefts:

然后根据是否存在左和右来匹配生成的左和右分区元组是一个简单的问题:

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)

Details of the intermediate partitionMapstep:

中间partitionMap步骤的详细信息:

List(Success(10), Success(20), Failure(new Exception("error1"))).partitionMap(_.toEither)
// => (List[Throwable], List[Int]) = (List(java.lang.Exception: error1), List(10, 20))

回答by Filippo De Luca

These are my 2cents:

这些是我的 2cents:

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