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.
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.
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...
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)
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.
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)
}
}
}
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)