Scala-在循环内尝试捕获并获得收益
我正在使用一些第三方库编写一个Scala应用程序。当对该库中的集合进行迭代时,会发生异常,我希望忽略该异常,然后继续迭代。整个过程都在一个有收益的for循环中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
val myFuntionalSequence = for {
mailing <- mailingCollection
} yield (mailing.getName, mailing.getSubject)
我的问题是,您知道一种函数式的方法来尝试迭代for循环,并在出错时跳过该元素,只返回迭代工作的元素吗
谢谢,
Felix更新:正如Tim在评论中指出的那样,我更新了答案以纠正我对问题的误解。 正如我在评论中提到的,我将假设“继续迭代”的意思是不抛出异常,并且在该点之前仍然返回结果。我还假设您希望保持
迭代器的惰性
如果是这样的话,我会有效地修饰java.util.Iterator
以防止它们的bug,但会返回一个ScalaIterator
,这样您仍然有原始库所需要的惰性。以下行中的某些内容(非生产准备就绪):
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)
}
}
}
现在,假设您使用库调用returnMailing
实例,您应该将其包装在选项中
:
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)
}