如何使用模式匹配检查列表是否包含所有Some或None或两者?

时间:2014-07-02 12:47:09

标签: scala pattern-matching

列表中包含List[Option[String]]类型,可能包含SomeNone

val list:List[Option[String]] = List(Some("aaa"), None, Some("bbb"))
list match {
   case /*List with all Some*/ => println("all items are Some")
   case /*List with all None*/ => println("all items are None")
   case /*List with Some and None*/ => println("Contain both Some and None")
}

但我不知道怎么写。是否可以使用模式匹配?

4 个答案:

答案 0 :(得分:8)

您可以编写自定义提取器:

object AllSome {
  def unapply[T](l: List[Option[T]]) = l.forall(_.isDefined)
}

object AllNone {
  def unapply[T](l: List[Option[T]]) = l.forall(_ == None)
}

object Mixed {
  def unapply[T](l: List[Option[T]]) = !AllNone.unapply(l) && !AllSome.unapply(l)
}

并使用它们:

list match {
  case AllSome() => ???
  case AllNone() => ???
  case Mixed() => ???
}

答案 1 :(得分:3)

一种方法是将列表展平并将结果长度与原始长度进行比较,如下所示,

list.flatten.length match {
  case 0                      => println("All items are None")
  case len if len < l.length  => println("Contain both Some and None")
  case _                      => println("All items are Some")
}

<强>更新

要获取每个Some的内容,只需list.flatten,例如,

list.flatten
res: List(aaa, bbb)

List().flatten
res: List[Nothing] = List()

答案 2 :(得分:0)

如果您真的想要一个具有明确定义的语义并基于模式匹配的解决方案,您可以执行以下操作:

abstract class CollectionStatus
case object Empty extends CollectionStatus
case object AllSome extends CollectionStatus
case object AllNone extends CollectionStatus
case object Mixed extends CollectionStatus

object CollectionStatus {
  def default: CollectionStatus = Empty
}

def folder(status: CollectionStatus, o: Option[_]): CollectionStatus = {
  (status, o) match {
    case (Empty, Some(_)) => AllSome
    case (Empty, None) => AllNone
    case (AllSome, Some(_)) => AllSome
    case (AllNone, None) => AllNone
    case _ => Mixed
  }
}

以下是我将如何使用它:

List[Option[String]]().foldLeft(CollectionStatus.default)(folder _) //Empty
List(Option("foo"), Option("bar")).foldLeft(CollectionStatus.default)(folder _) //AllSome
List(Option("foo"), None).foldLeft(CollectionStatus.default)(folder _) //Mixed
List(None, None).foldLeft(CollectionStatus.default)(folder _) //AllNone

这可以通过使用尾递归函数替换foldLeft来进一步改进,该函数将累积列表的状态,并且如果它识别出列表是&#34; Mixed&而完成其计算而不遍历整个列表。 #34;已:

import scala.annotation.tailrec
def listStatus(list: List[Option[_]]): CollectionStatus = {
  @tailrec
  def inner(acc: CollectionStatus, ls: List[Option[_]]): CollectionStatus = {
    acc match {
      case Mixed => Mixed
      case s => {
        ls match {
          case Nil => s
          case h :: t => {
            inner(folder(s, h), t)
          }
        }
      }
    }
  }

  inner(CollectionStatus.default, list)
}

答案 3 :(得分:0)

val l: List[Option[String]] = List(Some("aaa"), None, Some("bbb"))
l.groupBy({
  case Some(_) => "s"
  case None => "n"
}).toList match {
  case List(_,_) => println("both")
  case List(a) => if( a._1 == "n") println("none") else println("some")
}