2010-11-03 30 views
12

Cách tốt nhất để xử lý ngoại lệ trong khi lặp qua một vòng lặp trong Scala là gì? Ví dụ, nếu tôi có một phương thức convert() có thể ném một ngoại lệ, tôi muốn bắt ngoại lệ đó, ghi lại nó và tiếp tục lặp lại. Có một cách "scala" để làm điều này?Scala - Bắt một ngoại lệ trong một bản đồ

Lý tưởng nhất, tôi muốn một cái gì đó giống như ...

val points: Seq[Point] = ... 
val convertedPoints: Seq[ConvertedPoint] = points.map(
    p => { 
    try { p.convert() } 
    catch { case ex: Exception => logger.error("Could not convert", ex) } 
}) 

Bạn không thể làm các mã trên vì nó không phải là một ánh xạ trực tiếp từ một danh sách những việc khác (bạn nhận lại Seq [Bất kỳ] trái với Seq [ConvertedPoint]). Mọi sự trợ giúp sẽ rất được trân trọng!

Cảm ơn!

Trả lời

5

Có lẽ bạn muốn có một bản đồ phẳng. Dưới đây là một ví dụ, nên xem làm thế nào nó có thể phù hợp với :-)

List(1,2,3,4).flatMap(x => if (x > 2) Some(x) else None) 

Trên đây sẽ sử dụng tác dụng phụ đăng nhập (in hoặc đặt trong một cái gì đó có thể thay đổi - nếu điều này được thực hiện, đảm bảo việc đánh giá được buộc!). Để tránh các tác dụng phụ và hãy cẩn thận, chức năng ánh xạ chỉ có thể là Point -> Either[CovertedPoint,Exception] và sau đó kết quả có thể được tách riêng với Seq.partition hoặc tương tự.

14

flatMap có lẽ là những gì bạn đang tìm kiếm, nhưng chức năng bản đồ đã đăng tác dụng phụ và những tác dụng phụ có thể không xảy ra ngay lập tức nếu điểm là một cái nhìn:

val convertedPoints = points.view.flatMap { p => 
    try { 
    Some(p.convert) 
    } catch { 
    case e : Exception => 
    // Log error 
    None 
    } 
} 
println("Conversion complete") 
println(convertedPoints.size + " were converted correctly") 

này sẽ in:

Conversion complete 
[Error messages] 
x were converted correctly 

Trong trường hợp của bạn, hãy thả chế độ xem và bạn có thể ổn. :)

Để chuyển đổi thành hàm thuần túy (không có tác dụng phụ), bạn có thể sử dụng Hoặc. Mặc dù tôi không nghĩ rằng nó có giá trị các nỗ lực ở đây (trừ khi bạn thực sự muốn làm điều gì đó với các lỗi), sau đây là một ví dụ khá hoàn chỉnh của việc sử dụng nó:

case class Point(x: Double, y: Double) { 
    def convert = { 
    if (x == 1.0) throw new ConversionException(this, "x is 1.0. BAD!") 
    else ConvertedPoint(x, y) 
    } 
} 
case class ConvertedPoint(x: Double, y: Double) 
class ConversionException(p: Point, msg: String) extends Exception(msg: String) 


val points = List(Point(0,0), Point(1, 0), Point(2,0)) 

val results = points.map { p => 
    try { 
    Left(p.convert) 
    } catch { 
    case e : ConversionException => Right(e) 
    } 
} 

val (convertedPoints, errors) = results.partition { _.isLeft } 

println("Converted points: " + convertedPoints.map(_.left.get).mkString(",")) 
println("Failed points: " + errors.map(_.right.get).mkString(",")) 
+0

Bạn đã đánh cắp câu trả lời của tôi, nhưng 1 cho giải thích nó tốt hơn :-) Chào mừng bạn đến với SO. –

+1

Đồng ý - câu trả lời của bạn chính xác 100% (và một nguồn cảm hứng), nhưng tôi nghĩ thêm chi tiết hơn là một câu trả lời khác thay vì chỉ là một bình luận. :) – DaGGeRRz

12

Thú vị mà tôi đã có rất nhiều rắc rối giải thích lợi thế của việc sử dụng scala.util.control.Exception trên try/catch và sau đó tôi bắt đầu thấy các câu hỏi tạo ra các ví dụ hoàn hảo từ chúng.

đây:

import scala.util.control.Exception._ 
List(1, 23, 5, 2, 0, 3, 2) flatMap (x => catching(classOf[Exception]) opt (10/x)) 

mã riêng của bạn sẽ trông như thế này:

val points: Seq[Point] = ... 
val convertedPoints: Seq[ConvertedPoint] = points.flatMap(
    p => handling(classOf[Exception]) by { ex => 
    logger.error("Could not convert", ex); None 
    } apply Some(p.convert) 
) 

Hoặc, nếu bạn cấu trúc lại nó:

val exceptionLogger = handling(classOf[Exception]) by { ex => 
    logger.error("Could not convert", ex); None 
} 
val convertedPoints: Seq[ConvertedPoint] = points.flatMap(p => exceptionLogger(Some(p.convert))) 
Các vấn đề liên quan