如何在Scala中以惯用方式重写此代码?

时间:2014-07-17 13:22:59

标签: scala exception-handling optional

假设我有一个可能抛出异常的函数f: Int => String。我想编写一个函数tryF(f: Int => String, arg: Int, defaultOpt: Option[String]),其工作原理如下:

  • 调用f(arg)并返回结果,如果它没有抛出异常
  • 如果调用因异常而失败,defaultOpt.isDefined则返回defaultOpt.get
  • 否则抛出异常

我写的如下:

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
  val r = scala.util.Try(f(arg))
  r.toOption orElse defaultOpt getOrElse r.get
}

有意义吗?你会改写吗?

5 个答案:

答案 0 :(得分:5)

你的解决方案很好,即使我个人觉得它不太可读。我会做那样的事情:

def tryF[A](f: =>A, defaultOpt: Option[A]): A = (Try(f), defaultOpt) match {
  case (Success(result), _)          => result
  case (Failure(_), Some(defValue))  => defValue
  case (Failure(t), None)            => throw t
}

请注意,它是不必要的冗长(我可能在某些情况下使用了通配符),但我认为您的用例值得一些自我记录代码。很容易理解触发每种情况的原因。

答案 1 :(得分:2)

一种冗长但易于理解的方式......

import scala.util.{Try,Success,Falure}

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
 Try(f(arg)) match {
      case Success(r) => r
      case Failure(ex) => defaultOpt match {
           case Some(content) => content
           case None => throw ex
      } 
 }

稍微紧凑的方式:

import scala.util.{Try,Success,Falure}

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = 
 Try(f(arg)) match {
      case Success(r) => r
      case Failure(ex) => defaultOpt getOrElse {throw ex}
 }

答案 2 :(得分:2)

在您的情况下无需Try; try / catch就够了:

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
  try {
    f(arg)
  } catch {
    case e: Exception => defaultOpt getOrElse { throw e }
  }
}

如果您想使用Try,我建议您利用recover

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
  Try {
    f(arg)
  }.recover({
    case _ if defaultOpt.isDefined => defaultOpt.get
  }).get
}

但是,如果这是你的实际功能,我会考虑重写参数类型是通用的,而不是重写实现,只要实现已经有效:

def tryF[A, B](f: A => B, arg: A, defaultOpt: Option[B]): B = /* ... */

答案 3 :(得分:1)

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {

    val r = scala.util.Try(f(arg))

    if (r.isSuccess) r.get else defaultOpt.getOrElse(r.get)

    // reads like -> if r is success, get from r, else get from default, else get from r
}

答案 4 :(得分:1)

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
  val r = scala.util.Try(f(arg))
  (r, defaultOpt) match {
    case (Success(result), _) => result
    case (Failure(_), Some(opt)) => opt
    case (Failure(ex), None) => throw ex
}