Scala pattern matching sur des séquences autres que des listes

J'ai le code suivant qui fonctionne récursivement sur chaque élément d'une liste

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

Maintenant, ignorant que cette fonctionnalité est disponible par le biais de filtre() et foreach(), cela fonctionne bien. Cependant, si j'essaie de la modifier pour accepter toutes les Seq[Int], je rencontre des problèmes:

  • Seq n'a pas::, mais il a+:, ce qui, si je comprends bien, est fondamentalement la même chose. Si j'essaie de faire correspondre sur head +: tail cependant, le compilateur se plaint de l'erreur: non trouvé: valeur +: '
  • Nil est spécifique à List, et je ne suis pas sûr de quoi le remplacer. Je vais essayer Seq () si jamais je dépasse le problème précédent

Voici à quoi je pense que le code devrait ressembler, sauf qu'il ne fonctionne pas:

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

Edit: tant de bonnes réponses! J'accepte la réponse d'agilesteel car elle était la première à noter que:: n'est pas un opérateur dans mon exemple, mais une classe de cas et donc la différence.

47
demandé sur Zecrates 2011-07-24 18:56:12

6 réponses

Il y a deux :: (cons prononcés) dans Scala. L'un est un opérateur défini dans class List un classe (sous-classe de List), ce qui représente une liste non vide caractérisé par une tête et une queue.

head :: tail est un constructeur modèle, qui est syntaxiquement modifié à partir de ::(head, tail).

:: est une classe de cas, ce qui signifie qu'un objet extracteur est défini pour cela.

21
répondu agilesteel 2011-07-24 20:26:54

Genre de tricherie, mais ça y est:

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

Ne me demandez pas pourquoi xs* ne fonctionne pas...

48
répondu Landei 2011-07-24 16:24:56

Depuis les IDE de mars 2012, cela fonctionne en 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.

Plus généralement, deux objets head / tail et INIT / last decomposition différents en miroir append / prepend ont été ajoutés pour Seq dans SeqExtractors :

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)
41
répondu yakshaver 2013-10-02 21:52:52

Vous pouvez réellement définir un objet pour +: pour faire exactement ce que vous cherchez:

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)

Ensuite, votre code fonctionne exactement comme prévu.

Cela fonctionne parce que h +: t est équivalent à +:(h,t) lorsqu'il est utilisé pour patten correspondant.

23
répondu dhg 2015-04-10 04:34:05

Je ne pense pas qu'il existe un support de correspondance de motifs pour les séquences arbitraires dans la bibliothèque standard. Vous pouvez le faire sans correspondance de motif:

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

Vous pouvez définir vos propres objets Extracteurs. Voir 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)
    }
  }
}
4
répondu Kim Stebel 2011-07-24 20:45:13

Une simple transformation de Seq en List ferait le travail:

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)
-1
répondu user unknown 2011-07-25 14:41:05