56 votes

Correspondance des motifs Scala sur des séquences autres que des listes

J'ai le code suivant qui fonctionne de manière récursive sur chaque élément dans 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 à travers le filtre() et foreach(), cela fonctionne bien. Cependant, si j'essaie de la modifier pour accepter toute Seq[Int], je rencontre des problèmes:

  • Seq n'a pas ::, mais elle n'a +:, qui, comme je comprends, c'est fondamentalement la même chose. Si j'essaie de match sur la tête +: queue toutefois, le compilateur se plaint 'erreur: non trouvé: valeur +:'
  • Nil est spécifique à la Liste, et je ne suis pas sûr de ce que pour le remplacer. Je vais essayer de Seq() si j'ai jamais passé le problème précédent

Voici comment je pense que le code devrait ressembler, sauf que ça 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: autant de bonnes réponses! Je l'accepte agilesteel de répondre comme sa a été le premier qui a noté que :: n'est pas un opérateur dans mon exemple, mais d'un cas de classe et donc de la différence.

52voto

yakshaver Points 936

Depuis mars 2012, cela fonctionne dans la version 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 de décomposition tête / queue et init / dernière décomposition 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)
 

51voto

Landei Points 30509

Une sorte de triche, 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 ...

25voto

agilesteel Points 8330

Il y a deux :: (prononcé contre) en Scala. L'un est un opérateur défini en class List et l'autre est une classe (sous-classe de la 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és à partir d' ::(head, tail).

:: est une affaire de classe, ce qui signifie qu'il est un extracteur d'objet définis.

24voto

dhg Points 26700

Vous pouvez en fait définir un objet pour que +: fasse exactement ce que vous recherchez:

 object +: { 
  def unapply[T](s: Seq[T]) = 
    if(s.size >= 1)
      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 la correspondance de motifs.

4voto

Kim Stebel Points 22873

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

   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 cependant définir vos propres objets d'extraction. 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)
    }
  }
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X