Scala

时间:2015-05-17 20:22:44

标签: list scala pattern-matching

我对列表前缀的模式匹配(即列表的前几个元素)有疑问。

这会编译,但它无法正常工作:

  val l = List(1,2,3)

  val test = { m: List[Int] =>
    m match {
      case l :: tail => println("tail: "+tail.mkString(","))
      case _ => println("no match")
    }
  }

  test(List(1,2,3,4,5))

输出为tail: 2,3,4,5。我希望它可以说tail: 4,5,或者说无法匹配,或者在编译时失败。是什么让它成功呢?

我的第二个问题是:如何使用列表匹配列表前缀?我知道这可以按照我的预期运作:

      case 1 :: 2 :: 3 :: tail => println("tail: "+tail.mkString(","))
但是,我的前缀是列表,不能对它们进行硬编码。模式匹配在这里是否正确?

我知道我可以做类似

的事情
    if (m startsWith l) {
      val tail = m drop l.size          
    }

但是很长一段时间似乎都不太优雅,特别是在斯卡拉。

3 个答案:

答案 0 :(得分:4)

关于第一个代码段的输出,l内的match实际上是一个新值,它会影响外部范围l并在执行期间捕获1

您遇到的问题是::unapply的{​​{1}}将其分解为一个 head 值和尾部,解构链表。

虽然有一个List操作与:::一起连接两个列表,但它没有相应的:::,它可以让你在模式匹配中使用它你渴望的方式。

答案 1 :(得分:3)

我不认为这是可能的。我可以基于这种解决方法提出最接近的语法:

import collection.SeqLike

implicit class PrefixMatcher[T](prefix: Seq[T]) {
  object then  {
    def unapply[S <: SeqLike[T,S]](seq: S): Option[S] =
      if (seq startsWith prefix) Some(seq drop prefix.length) else None
  }
}

然后你可以用它作为

val test: List[Int] => Unit = {
  case l.then(tail) => println("tail: " + tail.mkString(","))
  case _ => println("no match")
}

答案 2 :(得分:0)

添加问题的第一部分:正如@Arne所提到的,case l未与您的列表匹配,但会捕获新列表。对于前者,你需要把它放在反叛上,但即便如此,我也看不到你如何达到你想要的效果,我能想到的关闭是:

 case `l` :+ x => println(s"tail is $s") //but this just works when the tail is just one element long. 

对于你问题的最后部分,也许模式匹配不是正确的做法,如何:

 val prefixes = List (List (1,2,3), List(4,5,6)...)

 def findTail(li:List[Int]) =  prefixes.collectFirst{ case p if li.startsWith(p) => li.drop(p.size) } //Option[List[Int]] 

如果没有匹配,这将找到与testListNone匹配的第一个前缀的尾部。您可以轻松地将其概括为仅使用Int以上的内容。