collection 内的模式匹配

Patttern matching within collection

我想在 Seq 中寻找特定的模式。我尝试同时使用 :+ 和 +: 运算符,但即使编译它似乎也不起作用,现在我必须首先依赖 'dropwhile' 操作,然后在collection.

在 Scala 中不可能写出类似的东西吗? 'from' 和 'to' 是现有变量

case beginColl :+ `from` +: someElement +: `to` +: tail => true 

编辑:它是 objects 的序列,而不是列表

我认为您可能需要识别这样的序列:

val test = Seq("x", "from", "y", "to", "z")

test match {
  case _ :: "from" :: _ :: "to" :: _ => true
  case _ => false
}

但是因为你需要知道序列是否有特定的特征,我会试试这个:

test match {
  case list if (list.indexOf("from") < list.indexOf("to")) => true
  case _ => false
}

这应该有效:

case Seq(_, `from`, _, `to`, _*) => true

编辑:

如果 'from' 之前有更多元素,dropWhile 是一个很好的解决方案,另一种(但效率较低)的方法可能是:

def matchSeq[T](s: Seq[T]): Boolean = s match { 
    case Seq(_, `from`, _, `to`, _*) => true
    case Seq(a, b@_*) => matchSeq(b) 
    case _ => false 
}

您可以使用containsSlice来检查序列是否包含子序列,或者您可以比较要查找的元素的索引。即:

val test = Seq("x", "from", "y", "to", "z")
val test2 = Seq("u", "w", "x", "from", "y", "to", "z")

test match {
  case s if s.indexOf("to") - s.indexOf("from") == 2 => true
  case _ => false
} //true in both cases.

这在该定义中永远不会起作用,因为您不能在 unapplySeq 的结果中通配除尾部之外的任何子序列。 但让我提出一个解决方法。

让我们定义这个助手:

object Span {
  class Spanner[T](pred: T => Boolean) {
    def unapply(seq: Seq[T]) = for {
      idx <- Some(seq.indexWhere(pred)) if idx >= 0
      (start, elem +: end) = seq.splitAt(idx)
    } yield (start, end)
  }
  def apply[T](pred: T => Boolean) = new Spanner(pred)
}

这让我们可以像这个函数一样定义更多有趣的匹配器:

def splitter[T](from:T, to:T): PartialFunction[Seq[T], Seq[Seq[T]]] = {
  val From = Span[T](_ == from)
  val To = Span[T](_ == to)

  {
    case From(prefix, To(middle, postfix)) => Seq(prefix, middle, postfix)
  }
}

因此,如果我们将其专门化为:

val mySplitter = splitter("from", "to").lift

我们可以得到合适的结果:

mySplitter(Seq("1", "2", "to", "3", "4", "from", "5", "6")) // None
mySplitter(Seq("1", "2", "from", "3", "4", "to", "5", "6")) // Some(List(List(1, 2), List(3, 4), List(5, 6))) 

让我们尝试阐明编译器如何理解您的语法,让我们定义

def splitter2(from: AnyRef, to: AnyRef): PartialFunction[Seq[_], AnyRef] = {
  case beginColl :+ `from` +: someElement +: `to` +: tail => (beginColl, someElement, tail)
}
val mySplitter2 = splitter2("from", "to").lift

所以如果我们尝试匹配

mySplitter2(Seq("1", "2", "from", "3", "4 ", "to", "5", "6"))

我们一定会得到None

但如果我们尝试

mySplitter2(Seq("1", "2", Seq("from", "3", "to", "4", "5")))

我们突然 Some(...)

所以编译器只是将你的表达式理解为 _match 元素

beginColl :+ __last

然后将 __last 匹配为

`from` +: someElement +: `to` +: tail

这基本上是 验证这是非空的 Seq 最后一个元素是 另一个 Seq 至少包含三个元素,其中第一个和第三个是 fromto