除列表之外的序列上的Scala模式匹配

Zec*_*tes 56 collections scala pattern-matching

我有以下代码,它们对List中的每个元素进行递归操作

def doMatch(list: List[Int]): Unit = list match {
  case last :: Nil  => println("Final element.")
  case head :: tail => println("Recursing..."); doMatch(tail)
}
Run Code Online (Sandbox Code Playgroud)

现在,忽略通过filter()foreach()可以使用此功能,这可以正常工作.但是,如果我尝试将其更改为接受任何Seq [Int],我会遇到问题:

  • Seq没有::,但确实有+:,据我所知基本上是相同的.如果我尝试匹配head +:tail然而,编译器会抱怨'错误:找不到:value +:'
  • Nil是List特有的,我不知道该替换它.如果我遇到上一个问题,我将尝试Seq()

以下是我认为代码的外观,除非它不起作用:

def doMatch(seq: Seq[Int]): Unit = seq match {
  case last +: Seq() => println("Final element.")
  case head +: tail  => println("Recursing..."); doMatch(tail)
}
Run Code Online (Sandbox Code Playgroud)

编辑:这么多好的答案!我接受了agilesteel的答案,因为他是第一个注意到::在我的例子中不是运算符,而是一个案例类,因此差异.

Lan*_*dei 51

有点作弊,但在这里:

def doMatch(seq: Seq[Int]): Unit = seq match {
  case Seq(x) => println("Final element " + x)
  case Seq(x, xs@_*) => println("Recursing..." + x); doMatch(xs)
}
Run Code Online (Sandbox Code Playgroud)

不要问我为什么xs*不工作......

  • @Zecrates:`_*`表示"忽略模式的以下元素".使用`@`语法我给"忽略"部分一个名字,这就像作弊一样.我不认为这个代码实际上有任何*错误*(当然`head`和`tail`在给定的`Seq`上可能效率低下). (2认同)

yak*_*ver 51

截至2012年3月的想法,这适用于2.10+:

  def doMatch(seq: Seq[Int]): Unit = seq match {
    case last +: Seq() => println("Final element.")
    case head +: tail  => println("Recursing..."); doMatch(tail)
  }                                               //> doMatch: (seq: Seq[Int])Unit

  doMatch(List(1, 2))                             //> Recursing...
                                                  //| Final element.
Run Code Online (Sandbox Code Playgroud)

更一般地,SeqSeqExtractors中添加了两个不同的head/tail和init/last分解对象镜像append/prepend :

List(1, 2) match { case init :+ last => last } //> res0: Int = 2                                              
List(1, 2) match { case head +: tail => tail } //> res1: List[Int] = List(2)                                               
Vector(1, 2) match { case init :+ last => last } //> res2: Int = 2                                              
Vector(1, 2) match { case head +: tail => tail } //> res3: scala.collection.immutable.Vector[Int] = Vector(2)
Run Code Online (Sandbox Code Playgroud)


agi*_*eel 25

::Scala中有两个(发音为cons).一个是定义的运算符class List,一个是(子类List),它表示以头和尾为特征的非空列表.

head :: tail是一个构造函数模式,从中进行语法修改::(head, tail).

:: 是一个案例类,这意味着为它定义了一个提取器对象.


dhg*_*dhg 24

您实际上可以定义一个对象,+:以便完全按照您的要求进行操作:

object +: { 
  def unapply[T](s: Seq[T]) = 
    if(s.nonEmpty)
      Some(s.head, s.tail) 
    else
      None
}

scala> val h +: t = Seq(1,2,3)
h: Int = 1
t: Seq[Int] = List(2, 3)
Run Code Online (Sandbox Code Playgroud)

然后您的代码完全按预期工作.

这是有效的,因为h +: t它等同于+:(h,t)用于模式匹配的时候.