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

时间:2011-07-24 14:56:12

标签: scala collections pattern-matching

我有以下代码,它们对List

中的每个元素进行递归操作
def doMatch(list: List[Int]): Unit = list match {
  case last :: Nil  => println("Final element.")
  case head :: tail => println("Recursing..."); doMatch(tail)
}

现在,忽略通过 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)
}

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

6 个答案:

答案 0 :(得分:50)

有点作弊,但在这里:

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

不要问我为什么xs*不起作用......

答案 1 :(得分:48)

截至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.

更一般地,在SeqExtractors中为Seq添加了两个不同的head / tail和init / last分解对象镜像追加/前置:

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)

答案 2 :(得分:24)

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

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

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

答案 3 :(得分: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)

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

这是有效的,因为h +: t在用于模式匹配时相当于+:(h,t)

答案 4 :(得分:4)

我认为标准库中的任意序列都没有模式匹配支持。你可以通过模式匹配来实现:

  def doMatch(seq: Seq[Int]) {
    if (seq.size == 1) println("final element " + seq(0)) else {
      println("recursing")
      doMatch(seq.tail)
    }
  }
  doMatch(1 to 10)

您可以定义自己的提取器对象。见http://www.scala-lang.org/node/112

object SEQ {
  def unapply[A](s:Seq[A]):Option[(A, Seq[A])] = {
    if (s.size == 0) None else {
      Some((s.head, s.tail))
    }
  }
}

def doMatch(seq: Seq[Int]) {
  seq match {
    case SEQ(head, Seq()) => println("final")
    case SEQ(head, tail) => {
      println("recursing")
      doMatch(tail)
    }
  }
}

答案 5 :(得分:-1)

从Seq到List的简单转换可以完成这项工作:

def doMatch (list: List[Int]): Unit = list match {           
    case last :: Nil => println ("Final element.")             
    case head :: tail => println ("Recursing..."); doMatch (tail)
    case Nil => println ("only seen for empty lists") 
  }

def doMatchSeq (seq: Seq[Int]) : Unit = doMatch (seq.toList)

doMatch (List(3, 4, 5))
doMatchSeq (3 to 5)