web-dev-qa-db-fra.com

Scala correspondance de motifs sur des séquences autres que les 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, en ignorant que cette fonctionnalité est disponible via filter () et foreach (), cela fonctionne très bien. Cependant, si j'essaye de le changer pour accepter n'importe quel Seq [Int], je rencontre des problèmes:

  • Seq n'a pas ::, mais il a + :, ce qui, si je comprends bien, est essentiellement la même chose. Si j'essaie de faire correspondre sur head +: tail cependant, le compilateur se plaint "erreur: introuvable: valeur +:"
  • Nil est spécifique à List, et je ne sais pas par quoi le remplacer. Je vais essayer Seq () si 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 la sienne a été la première à noter que :: n'est pas un opérateur dans mon exemple, mais une classe de cas et donc la différence.

53
Zecrates

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

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

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

24
agilesteel

Une sorte de tricherie, mais voilà:

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

51
Landei

Depuis les ides 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 tête/queue et init/dernière décomposition 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)
49
yakshaver

Vous pouvez réellement définir un objet pour que +: Fasse 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 car h +: t Est équivalent à +:(h,t) lorsqu'il est utilisé pour la correspondance de patten.

24
dhg

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 motifs:

  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 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
Kim Stebel