Scala:匿名函数中的异常处理

时间:2013-06-19 06:07:23

标签: debugging scala exception functional-programming

如果我将匿名函数作为参数传递,例如在此代码示例中:

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

4 个答案:

答案 0 :(得分: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 => ...
}

答案 1 :(得分: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)
}

但是如果你要从文本文件中导入数据,我会尝试这样做而没有例外(因为在这种情况下获取无效数据并不是特例):

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

答案 2 :(得分: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))

// ...

答案 3 :(得分:2)

首先你的外部尝试/捕获是没用的。如果列表(或其他结构)为空,map函数将不执行任何操作=&gt;不会抛出ArrayIndexOutOfBoundsException

至于内循环,我会使用Scalaz Either:

来获取另一个解决方案
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))
  }
}

然后将您的操作链接到List [EitherT [...]]