2013-06-19 3 views
1

Если я передаю анонимную функцию в качестве аргумента, например, например. в этом примере кода:Scala: обработка исключений в анонимной функции

val someMap = someData.map(line => (line.split("\\|")(0), // key 
            line.split("\\|")(1) + "|" + // value as string concat 
            line.split("\\|")(4) + "|" + 
            line.split("\\|")(9))) 

Я мог бы поймать, например. ArrayIndexOutOfBoundsException так:

try { 
    val someMap = someData.map(line => (line.split("\\|")(0), // key 
             line.split("\\|")(1) + "|" + // value as string concat 
             line.split("\\|")(4) + "|" + 
             line.split("\\|")(9))) 
} catch { 
    case e1: ArrayIndexOutOfBoundsException => println("exception in line ") 
} 

Проблема в том, что у меня нет доступа к сферам внутренней функции. В этом случае я хотел бы напечатать line (от анонимной функции), которая вызвала исключение.

Как это сделать? Есть ли способ поймать исключение в анонимной функции? Есть ли способ получить доступ к области анонимной функции извне для целей отладки?

редактировать: Я использую Scala 2.9.3

ответ

3

Вы можете использовать Either

val result = 
    someData.map { 
    line => 
     try { 
     val values = (line.split("\\|")(0), // key 
      line.split("\\|")(1) + "|" + // value as string concat 
      line.split("\\|")(4) + "|" + 
      line.split("\\|")(9)) 
     Right(values) 
     } catch { 
     case e1: ArrayIndexOutOfBoundsException => 
      Left(s"exception in line $line") 
     } 
    } 

result.foreach { 
    case (Right(values)) => println(values) 
    case (Left(msg)) => println(msg) 
} 

Но если вы импортируете данные из текстового файла, я бы попытался сделать это без исключений (потому что на самом деле не исключено получение недопустимых данных в th в случае):

val result = 
    someData.map { 
    line => 
     val fields = line.split("\\|") 
     if (fields.length < 9) { 
     Left(s"Error in line $line") 
     } else { 
     val values = (fields(0), Seq(fields(1), fields(4), fields(9))) 
     Right(values) 
     } 
    } 

result.foreach { 
    case (Right((key, values))) => println(s"$key -> ${values.mkString("|")}") 
    case (Left(msg)) => println(msg) 
} 
3

Возможно, это даст вам некоторые идеи:

try { 
    val someMap = someData.map { line => 
      try { 
       (line.split("\\|")(0), // key 
       line.split("\\|")(1) + "|" + // value as string concat 
       line.split("\\|")(4) + "|" + 
       line.split("\\|")(9))) 
      } catch { 
       case inner: ArrayIndexOutOfBoundsException => { 
         println("exception in " + line) 
         throw inner; 
        } 
      } 
     } 
} catch { 
    case outer: ArrayIndexOutOfBoundsException => ... 
} 
2

Первый ваш внешний Try/улов бесполезно. Если List (или другая структура) пуст, функция map ничего не сделает => no ArrayIndexOutOfBoundsException будет выброшена.

Что касается внутреннего цикла, я бы sugest другого решения с Scalaz Либо:

import scalaz._ 
import EitherT._ 
import Id.Id 

val someMap = someData.map { line => 
    fromTryCatch[Id, (String, String)] { 
    (line.split("\\|")(0), // key 
     line.split("\\|")(1) + "|" + // value as string concat 
     line.split("\\|")(4) + "|" + 
     line.split("\\|")(9)) 
    } 
} 

, а затем цепь вам операции Списка [EitherT [...]]

+0

Он цеплялись 'ArrayIndexOutOfBoundsException' приходит индексировать строки, как в' line.split (...) (3) ', а не' map'. –

+0

@ PetrPudlák, вот почему ему нужно обернуть внутреннюю анонимную функцию, попробуйте/поймайте карту, не требуемую – 4lex1v

3

Другие ответы дают хорошие функциональные решения с использованием Either и т.д. Если вы используете Scala 2.10, можно также использовать в качестве Try

val lines = List("abc", "ef"); 
println(lines.map(line => Try(line(3)))); 

получить List[Try[Char]], где вы можете изучить каждый элемент, если он преуспел или потерпел неудачу. (Я не пробовал компилировать это.)


Если по каким-либо причинам вы предпочитаете исключения, вам нужно поймать исключение внутри функции отображения и повторно выдать ему информацию о линии. Например:

// Your own exception class holding a line that failed: 
case class LineException(line: String, nested: Exception) 
    extends Exception(nested); 

// Computes something on a line and throw a proper `LineException` 
// if the processing fails: 
def lineWorker[A](worker: String => A)(line: String): A = 
    try { 
    worker(line) 
    } catch { 
    case (e: Exception) => throw LineException(line, e); 
    } 

def getNth(lines: List[String], i: Int): List[Char] 
    = lines.map(lineWorker(_.apply(i))); 

val lines = List("abc", "ef"); 
println(getNth(lines, 1)); 
println(getNth(lines, 2)); 

Вы также можете выразить его с помощью Catch из scala.util.control.Exception:.

case class LineException(line: String, nested: Throwable) 
    extends Exception(nested); // we need Throwable here ^^ 

import scala.util.control.Exception._ 

// Returns a `Catch` that wraps any exception to a proper `LineException`. 
def lineExceptionCatch[T](line: String): Catch[T] 
    = handling[T](classOf[Exception]).by(e => throw LineException(line, e)); 

def lineWorker[A](worker: String => A)(line: String): A = 
    lineExceptionCatch[A](line)(worker(line)) 

// ... 
Смежные вопросы