2016-09-28 59 views
1

我在玩斯卡拉懶惰的視圖。看來,如果我們在轉換過程中遇到異常情況,處理起來並不容易。 我試圖嘗試包,但沒有運氣:在懶惰視圖轉換中處理異常

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) 

我缺少什麼?

回答

1

你不錯過任何東西;這就是視圖的設計方式。他們懶洋洋地調用引發異常的方法,但他們只需取結果並將其傳遞給下一個映射的方法。對於一個異常做任何事情都已經太晚了,這種異常在回饋結果之前就已經脫離了控制流程。

如果你真的需要處理這樣的情況下,你才能把事情甚至通過使用迭代器和手動纏繞在Trynext呼叫懶惰。 (取決於實現方式,您可能還需要包裝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),它會更順利地工作。

如果你想保持懶惰的w水平,嘗試實現它,像這樣:

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

或簡單地寫換到在Try塊危險的迭代器調用一個自定義的迭代器。

+0

謝謝!聽起來很明顯... 結束了,我實現了你的最後一個建議(自定義迭代器在下一步()中嘗試)。奇蹟般有效 – mathieu

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