我对列表前缀的模式匹配(即列表的前几个元素)有疑问。
这会编译,但它无法正常工作:
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
}
但是很长一段时间似乎都不太优雅,特别是在斯卡拉。
答案 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]]
如果没有匹配,这将找到与testList
或None
匹配的第一个前缀的尾部。您可以轻松地将其概括为仅使用Int
以上的内容。