处理延迟视图转换中的异常

时间:2016-09-28 21:29:21

标签: scala exception-handling scala-collections

我正在玩scala lazy views。似乎如果我们在转换过程中遇到异常,那么处理它并不容易。 我尝试用Try包装,但没有运气:

var v = (1 to 10).view.map {
  case 5 => throw new Exception("foo")
  case v => v
}

val w = v.map { w => Try(w) }

w.foreach { x =>
  if (x.isFailure)
    println("got it")
  else
    println(x.get)
}

结果:

v: scala.collection.SeqView[Int,Seq[_]] = SeqViewM(...)

w: scala.collection.SeqView[scala.util.Try[Int],Seq[_]] = SeqViewMM(...)

1
2
3
4
java.lang.Exception: foo
    at #worksheet#.$anonfun$1.apply$mcII$sp(tt.sc0.tmp:4)
    at #worksheet#.$anonfun$1.apply(tt.sc0.tmp:3)
    at #worksheet#.$anonfun$1.apply(tt.sc0.tmp:3)
    at scala.collection.TraversableViewLike$Mapped$$anonfun$foreach$2.apply(tt.sc0.tmp:165)
    at scala.collection.Iterator$class.foreach(tt.sc0.tmp:889)
    at scala.collection.AbstractIterator.foreach(tt.sc0.tmp:1332)
    at scala.collection.IterableLike$class.foreach(tt.sc0.tmp:68)
    at scala.collection.SeqLike$$anon$2.foreach(tt.sc0.tmp:667)
    at scala.collection.TraversableViewLike$Mapped$class.foreach(tt.sc0.tmp:164)
    at scala.collection.SeqViewLike$$anon$3.foreach(tt.sc0.tmp:193)
    at scala.collection.TraversableViewLike$Mapped$class.foreach(tt.sc0.tmp:164)
    at scala.collection.SeqViewLike$$anon$3.foreach(tt.sc0.tmp:193)
    at #worksheet#.#worksheet#(tt.sc0.tmp:8)

我错过了什么?

2 个答案:

答案 0 :(得分:1)

你不会遗漏任何东西;这是视图的设计方式。他们懒惰地调用抛出异常的方法,但他们只是获取结果并将其传递给下一个地图的方法。对于异常做任何事都为时已晚,在返回结果之前,这种异常会退出控制流程。

如果你真的需要处理这样的情况,可以使用迭代器并在next中手动包装Try调用,使更加懒惰。 (根据实现情况,您可能还需要包装hasNext - 例如,过滤器可能会在过滤掉的内容中引发异常。)

val wb = Seq.newBuilder[Try[Int]]
val vi = v.iterator
while (vi.hasNext) wb += Try{ vi.next }
val w = wb.result

否则,如果您可以通过返回指示失败的值来重新设计您的视图以指示失败(例如Left(ohDear) Right(yay)是好的值;或者只是使用Option),那么& #39;工作得更顺利。

如果您希望将懒惰保持在w级别,请尝试执行此操作:

val vi = v.iterator
val w = Iterator.continually(Try(vi.next)).takeWhile(_ => vi.hasNext)

或者只是编写一个自定义迭代器来封装Try块中对危险迭代器的调用。

答案 1 :(得分:0)

具有延迟视图的东西是在某些consumer消耗它之前不会评估底层迭代器。 mapflatMap等不是消费者,transformerlazy view转换为另一个lazy view

消费者包括foreachfold等。只有当消费者使用视图时,才会执行实际的转换步骤。所以你只需要在Try

中包含你的消费电话
val v = (1 to 10).view.map {
  case 5 => throw new Exception("foo")
  case v => v
}

val w = v.map(_ + 10)

Try(w.foreach(i =>
  println(i)
)) match {
  case Success(_) => println("Successfully done.")
  case Failure(ex) => println("I tried so hard... but it doesn't even matter")
}

并且...... Try的问题是,你应该用它包装问题创建者,以便它可以防止任何可能的异常。所以你应该做的是用Try包裹变换。

val view1 = (1 to 10).view

val view2 = view1.map(v => Try(v match {
  case 5 => throw new Exception("foo")
  case v => v
}))

// Now, your view2 is actually a view of Try's

// you can map it to transform again

val view3 = view2.map({
  case Success(v) => v + 20
  case Failure(ex) => throw ex
})

// now consume

view3.foreach({
  case Success(v) => println(v)
  case Failure(ex) => println("this one is a bad boy")
})