更简洁的方法来匹配和访问最后的选项[列表]

时间:2015-07-21 20:57:11

标签: scala refactoring

我有一个函数,作为参数获取一个对象,如果它是正确的类型,我需要访问Option[List[Int]]中的最后一个元素。我有一个有效的解决方案,但它看起来很笨拙。如果obj.listOfThings中没有任何项目,我需要i的值为0.有没有更好的方法来实现这一目标?

val i = foo match {
  case obj: Bar =>
    obj.listOfInts match {
      case Some(ints) =>
        ints.last
      case _ =>
        0
    }
  case _ =>
    0
}

从技术上讲,它可以返回Option[Int]。我对Scala还很陌生,并希望学习更好的方法解决这类问题。

4 个答案:

答案 0 :(得分:4)

在你的情况下,似乎Ende Neu建议的是正确的方法:

val i = foo match {
  case obj: Bar =>
     obj.listOfInts.map(_.last /* This throws an exception when the list is empty*/).getOrElse(0)
  case _ =>
    0
}

但是如果你调查一下,你会发现你的代码中有一个错误,在那个obj.listOfInts is Some(Nil)的情况下,因为在这种情况下你得到一个NoSuchElementException试图在空列表上调用last 。

请使用foo = Bar(Some(Nil))试用此代码并亲眼看看。

当您使用Option [List]时,请仔细考虑这是否是您想要的。 通常在考虑之后你会废弃Option而只留下List,因为Option没有任何意义。 我与许多错误使用Option [List]的开发人员合作,因为他们不理解Nil和None之间的相似性,通常'None'案例最终扮演的角色与Some(Nil)相同

所以你最终不得不这样做:

optionalList match {
    case None => // do something
    case Some(list) =>
        list match {
            case Nil => // do the same thing
            case head::tail => // do other stuff
        }
}

如您所见,无案例和Some(Nil)案例基本相同。

要修复您的错误,您应该这样做:

  case class Bar(listOfInts: Option[List[Int]])

  val i = foo match {
    case Bar(Some(list)) if list != Nil => list.last
    case _ => 0
  }

答案 1 :(得分:2)

您可能希望在此使用flatMaplastOption

obj.listOfInts.flatMap(_.lastOption)

如果listOfIntsNone,或者为Some(Nil),则会返回None。否则它将返回最后一个元素。如果您想返回0而不是None,请使用getOrElse

obj.listOfInts.flatMap(_.lastOption).getOrElse(0)

如果你想使用匹配,你可以这样做:

obj.listOfInts match {
    case Some(list@(hd::tl)) => list.last
    case _ => 0
}

此处,hd::tl保证list不为空。另一种选择是使用条件匹配:

obj.listOfInts match {
    case Some(list) if list.nonEmpty => list.last
    case _ => 0
}

或者首先匹配NoneSome(Nil)个案例:

obj.listOfInts match {
    case None | Some(Nil) => 0
    case Some(list) => list.last
}

答案 2 :(得分:1)

正如评论中所建议的那样,我认为最好的方法是:

val i = foo match {
  case obj: Bar => obj.listOfInts.map(_.last).getOrElse(0)
  case _        => 0
}

答案 3 :(得分:0)

更简洁的方式,包括instanceof:

scala> case class B(is: Option[List[Int]])
defined class B

scala> def f(x: Any) = Option(x) collect { case b: B => b.is flatMap (_.lastOption) } flatten
f: (x: Any)Option[Int]

scala> f(B(Option(5 to 7 toList)))
res0: Option[Int] = Some(7)

scala> import PartialFunction.{ condOpt => when }
import PartialFunction.{condOpt=>when}

scala> def g(x: Any) = when(x) { case b: B => b.is flatMap (_.lastOption) } flatten
g: (x: Any)Option[Int]

scala> g(B(Option(5 to 7 toList)))
res1: Option[Int] = Some(7)

可能值得问一下为什么你丢失了静态类型信息,你需要模式匹配。