Scala-在循环内尝试捕获并获得收益

Scala-在循环内尝试捕获并获得收益,scala,for-loop,try-catch,yield,Scala,For Loop,Try Catch,Yield,我正在使用一些第三方库编写一个Scala应用程序。当对该库中的集合进行迭代时,会发生异常,我希望忽略该异常,然后继续迭代。整个过程都在一个有收益的for循环中 val myFuntionalSequence = for { mailing <- mailingCollection } yield (mailing.getName, mailing.getSubject) 我的问题是,您知道一种函数式的方法来尝试迭代for循环,并在出错时跳过该元素,只返回迭代工作的元素吗 谢谢, Fe

我正在使用一些第三方库编写一个Scala应用程序。当对该库中的集合进行迭代时,会发生异常,我希望忽略该异常,然后继续迭代。整个过程都在一个有收益的for循环中

val myFuntionalSequence = for {
  mailing <- mailingCollection
} yield (mailing.getName, mailing.getSubject)
我的问题是,您知道一种函数式的方法来尝试迭代for循环,并在出错时跳过该元素,只返回迭代工作的元素吗

谢谢,
Felix更新:正如Tim在评论中指出的那样,我更新了答案以纠正我对问题的误解。

正如我在评论中提到的,我将假设“继续迭代”的意思是不抛出异常,并且在该点之前仍然返回结果。我还假设您希望保持
迭代器的惰性

如果是这样的话,我会有效地修饰
java.util.Iterator
以防止它们的bug,但会返回一个Scala
Iterator
,这样您仍然有原始库所需要的惰性。以下行中的某些内容(非生产准备就绪):

def scalaIterator[A](it: java.util.Iterator[A]): Iterator[Option[A]] = new Iterator[Option[A]](){
  private var hasBoomed: Boolean = false
  override def hasNext = !hasBoomed && it.hasNext

  override def next() = {
    Try(it.next()) match {
      case Failure(_) =>
        hasBoomed = true
        None
      case Success(value) => Some(value)
    }
  }
}
现在,假设您使用库调用return
Mailing
实例,您应该将其包装在
选项中

val myFuntionalSequence: Iterator[Option[(String, String)]] = for{
  mailingOpt <- scalaIterator(thirdPartyIterator)
} yield mailingOpt.map(mailing => (mailing.name, mailing.subject))
现在,如果您实际上不关心最后的
None
?如果您真正想要的是一个包含所有成功返回的
邮件的
列表[(String,Int)]
,该怎么办?在这种情况下,您将执行以下操作:

val flattened: Iterator[(String, String)] = myFuntionalSequence.flatten
现在是真正的功能部分;)

但是如果你真的想知道是否有例外呢?也就是说,如果至少有一个异常,您希望返回
None
,否则返回Some(列表(someMailing1Pair等…)。基本上,您需要的是一种将
List[Option[(String,String)]
转换为
Option[List[(String,String)]]
的方法

从Cats库中输入:

implicit cats.implicits._
val myFuntionalSeuence: Iterator[Option[(String, String)]] = ...
val flipped: Option[List[(String, String)]] = myFuntionalSeuence.toList.sequence //notice the .toList here: we are materializing a lazy collection here

…此时您将其视为任何其他
选项
。这种方法是通用的,它适用于任何实现<代码>遍历的工具,我建议您阅读我链接的文档:您会发现很多好东西!:)

如果希望保持功能性,则需要一个递归函数来取消选中不可靠的迭代器

这是未经测试的代码,但可能如下所示:

def safeIterate[T](i: Iterator[T]): List[Try[T]] = {
  @annotation.tailrec
  def loop(res: List[Try[T]]): List[Try[T]] =
    if (i.hasNext) {
      loop(Try(i.next) +: res)
    } else {
      res.reverse
    }

  loop(Nil)
}
您可以检查每个
Try
值,查看哪些迭代成功或失败。如果您只需要成功值,则可以调用
列表上的
.flatMap(u.toOption)
。或者使用此版本的
safeIterate

def safeIterate[T](i: Iterator[T]): List[T] = {
  @annotation.tailrec
  def loop(res: List[T]): List[T] =
    if (i.hasNext) {
      Try(i.next) match {
        case Success(t) => loop(t +: res)
        case _ => loop(res)
      }
    } else {
      res.reverse
    }

  loop(Nil)
}

比我聪明的人可能会让这个返回成为另一个
迭代器
,而不是
列表

什么是mailingCollection?若它只是一个集合,那个么不能在该行中抛出错误(除非是您自己的集合,它在
map
上引发了一个错误。可能
getName
getSubject
正在引发异常?嘿,Krzysztof,mailingCollection是来自该外部库的一个类的对象。因此,我认为实际的错误在迭代的实现中。但我只想跳过那个错误。
mailingCollection
很大吗?你们需要懒洋洋地处理它吗?或者你们可以把它加载到列表中吗?你们能分享
mailingCollection
的类型吗?我不知道你们怎么能“跳过”一个迭代。我的意思是,我可以想象如果你从迭代器中得到一个异常,你就不能再调用
。下一次
,现在得到一个元素,不是吗?也许是“继续迭代”您的意思是,在不引发异常的情况下处理错误?第一个表达式不会返回
列表
,它将返回一个
选项
。是的,我意识到我假设迭代已经在一个单独的行中。将修复答案
implicit cats.implicits._
val myFuntionalSeuence: Iterator[Option[(String, String)]] = ...
val flipped: Option[List[(String, String)]] = myFuntionalSeuence.toList.sequence //notice the .toList here: we are materializing a lazy collection here
def safeIterate[T](i: Iterator[T]): List[Try[T]] = {
  @annotation.tailrec
  def loop(res: List[Try[T]]): List[Try[T]] =
    if (i.hasNext) {
      loop(Try(i.next) +: res)
    } else {
      res.reverse
    }

  loop(Nil)
}
def safeIterate[T](i: Iterator[T]): List[T] = {
  @annotation.tailrec
  def loop(res: List[T]): List[T] =
    if (i.hasNext) {
      Try(i.next) match {
        case Success(t) => loop(t +: res)
        case _ => loop(res)
      }
    } else {
      res.reverse
    }

  loop(Nil)
}