我正在玩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)
我错过了什么?
答案 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
消耗它之前不会评估底层迭代器。 map
,flatMap
等不是消费者,transformer
将lazy view
转换为另一个lazy view
。
消费者包括foreach
,fold
等。只有当消费者使用视图时,才会执行实际的转换步骤。所以你只需要在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")
})