更简洁的方式类匹配和访问最后一个选项[列表]

问题描述:

我有一个函数,作为参数需要一个对象,如果它是正确的类型,我需要访问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仍然很陌生,想要学习更好的解决这类问题的方法。

+1

您可以避免与'obj.listOfInts.map(_。last).getOrElse(0)'的第二次匹配。 –

+0

感觉解决方案笨拙并需要改进是一个很好的信号,表明您正在学习某些东西。这可能是更多的代码审查问题,虽然.. – beefyhalo

+0

@beefyhalo你其实可能是正确的,我不会介意它被迁移到那里。我其实已经忘记了甚至存在。 – Rig

你的情况,最初似乎有什么恩德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 
} 

但是如果你仔细研究它,你会发现你的代码中有一个bug,如果是那个obj.listOfInts is Some(Nil),因为在这种情况下,你会得到一个NoSuchElementException,试图在空List上调用Last。

试试这个代码与foo = Bar(Some(Nil))并亲自看看。

当您使用选项[列表]认真思考如果这是你想要的。 通常在经过一番思考之后,您会放弃该选项,只留一个列表,因为该选项没有用处。 我曾与很多开发者滥用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 
     } 
} 

正如你所看到的无壳和一些(无)情况基本相同。

要解决你的错误,你应该做的:

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

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

好直觉。这个答案值得仔细阅读。 – beefyhalo

+0

是的,我没有写课,但我会去改变那个选项[列表]。如果我只是选择List [Int],我有另一个地方需要知道它的未分配或空。目前它检查无| |的情况有些(无)。从我对你的帖子的理解中,我可以摆脱这个选项,并且仅仅是一些(无)? – Rig

+0

不,我的意思是,如果它是你所有的代码,也许你可以把它保留为List [Int]。但是现在我看到你区分未分配(无)和空(一些(无))的情况。我真的不知道酒吧是如何实施的,所以很难回答,但我会尝试添加到我原来的答案。 –

正如意见建议,我认为最好的方法是:

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

你可能想使用flatMaplastOption这里:

obj.listOfInts.flatMap(_.lastOption) 

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

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

如果你想用一根火柴,你可以这样做:

obj.listOfInts match { 
    case Some([email protected](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 
} 
+0

's /可能/绝对'。 –

更简洁的方式包括的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) 

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

+0

它可能是应该被设计得更好一些的东西,但是我们有一些超类的几个不同的子类型,它们有一些在这个函数中有特殊的属性。 – Rig