1

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

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

4 回答 4

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 => ...
}
于 2013-06-19T06:53:58.187 回答
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)
}
于 2013-06-19T07:19:35.963 回答
3

其他答案使用等提供了很好的功能解决方案Either。如果您使用的是 Scala 2.10,您也可以使用Tryas

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

您也可以使用Catchfrom来表达它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))

// ...
于 2013-06-19T08:37:45.607 回答
2

首先,您的外部尝试/捕获是无用的。如果您的 List (或其他结构)为空,则map函数不会做任何事情 => noArrayIndexOutOfBoundsException将被抛出。

至于内部循环,我会建议使用 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[...]] 上链接你的操作

于 2013-06-19T07:17:14.930 回答