Recherche…


Syntaxe

  • sélecteur match partielleFonction
  • match de sélection {liste des alternatives à la casse) // C'est la forme la plus courante de ce qui précède

Paramètres

Paramètre Détails
sélecteur L'expression dont la valeur correspond à un motif.
des alternatives une liste d'alternatives délimitées par des case .

Match simple

Cet exemple montre comment faire correspondre une entrée à plusieurs valeurs:

def f(x: Int): String = x match {
  case 1 => "One"
  case 2 => "Two"
  case _ => "Unknown!"
}

f(2)  // "Two"
f(3)  // "Unknown!"

Démo en direct

Note: _ est la chute ou par cas par défaut, mais il est pas nécessaire.

def g(x: Int): String = x match {
  case 1 => "One"
  case 2 => "Two"
}

g(1)  // "One"
g(3)  // throws a MatchError

Pour éviter de lancer une exception, il est préférable de gérer le cas par défaut ( case _ => <do something> ). Notez que la correspondance sur une classe de cas peut aider le compilateur à générer un avertissement si une requête est manquante. Il en va de même pour les types définis par l'utilisateur qui étendent un trait scellé. Si la correspondance est totale, un cas par défaut peut ne pas être nécessaire

Il est également possible de faire correspondre les valeurs qui ne sont pas définies en ligne. Celles-ci doivent être des identifiants stables , obtenus soit en utilisant un nom en majuscule, soit en entourant les backticks.

Avec One et two définis ailleurs, ou passés en tant que paramètres de fonction:

val One: Int = 1
val two: Int = 2

Ils peuvent être comparés de la manière suivante:

def g(x: Int): String = x match {
  case One => "One"
  case `two` => "Two"
}

Contrairement à d'autres langages de programmation comme Java par exemple, il n'y a pas de chute. Si un bloc de requête correspond à une entrée, il est exécuté et la correspondance est terminée. Par conséquent, le cas le moins spécifique devrait être le dernier cas.

def f(x: Int): String = x match {
  case _ => "Default"
  case 1 => "One"
}

f(5) // "Default"
f(1) // "Default"

Correspondance de modèle avec un identifiant stable

Dans la correspondance de modèle standard, l'identificateur utilisé masquera tout identifiant dans la portée englobante. Parfois, il est nécessaire de faire correspondre la variable de l'étendue englobante.

L'exemple de fonction suivant prend un caractère et une liste de tuples et renvoie une nouvelle liste de tuples. Si le caractère existait en tant que premier élément dans l'un des n-uplets, le deuxième élément est incrémenté. S'il n'existe pas encore dans la liste, un nouveau tuple est créé.

def tabulate(char: Char, tab: List[(Char, Int)]): List[(Char, Int)] = tab match {
  case Nil => List((char, 1))
  case (`char`, count) :: tail => (char, count + 1) :: tail
  case head :: tail => head :: tabulate(char, tail)
}

L'illustration ci-dessus montre une correspondance de modèle où l'entrée de la méthode, char , est conservée 'stable' dans la correspondance de modèle: si vous appelez tabulate('x', ...) , la première instruction sera interprétée comme:

case('x', count) => ...

Scala interprétera toute variable délimitée par une coche comme identifiant stable: elle interprétera également toute variable commençant par une majuscule de la même manière.

Correspondance de motif sur une Seq

Pour rechercher un nombre précis d'éléments dans la collection

def f(ints: Seq[Int]): String = ints match {
  case Seq() =>
      "The Seq is empty !"
  case Seq(first) =>
      s"The seq has exactly one element : $first"
  case Seq(first, second) =>
      s"The seq has exactly two elements : $first, $second"
  case  s @ Seq(_, _, _) => 
      s"s is a Seq of length three and looks like ${s}"  // Note individual elements are not bound to their own names.
  case s: Seq[Int] if s.length == 4 =>
      s"s is a Seq of Ints of exactly length 4"  // Again, individual elements are not bound to their own names.
  case _ =>
      "No match was found!"
}

Démo en direct

Pour extraire le ou les premier (s) élément (s) et conserver le reste en tant que collection:

def f(ints: Seq[Int]): String = ints match {
  case Seq(first, second, tail @ _*) =>
      s"The seq has at least two elements : $first, $second. The rest of the Seq is $tail"
  case Seq(first, tail @ _*) =>
      s"The seq has at least one element : $first. The rest of the Seq is $tail"
  // alternative syntax
  // here of course this one will never match since it checks
  // for the same thing as the one above
  case first +: tail =>
      s"The seq has at least one element : $first. The rest of the Seq is $tail"
  case _ =>
      "The seq didn't match any of the above, so it must be empty"
}

En général, toute forme pouvant être utilisée pour construire une séquence peut être utilisée pour établir une correspondance avec une séquence existante.

Notez que lorsqu’on utilise Nil et :: fonctionnera lorsque le motif correspond à une séquence, il le convertit en une List et peut avoir des résultats inattendus. Limitez-vous à Seq( ...) et à +: pour éviter cela.

Notez que lorsque vous utilisez :: ne fonctionnera pas pour WrappedArray , Vector etc, consultez:

scala> def f(ints:Seq[Int]) = ints match {
     | case h :: t => h
     | case _ => "No match"
     | }
f: (ints: Seq[Int])Any

scala> f(Array(1,2))
res0: Any = No match

Et avec +:

scala> def g(ints:Seq[Int]) = ints match {
     | case h+:t => h
     | case _ => "No match"
     | }
g: (ints: Seq[Int])Any

scala> g(Array(1,2).toSeq)
res4: Any = 1

Gardes (si expressions)

Les instructions de cas peuvent être combinées avec des expressions if pour fournir une logique supplémentaire lors de la correspondance de modèle.

def checkSign(x: Int): String = {
    x match {
      case a if a < 0 => s"$a is a negative number"
      case b if b > 0 => s"$b is a positive number"
      case c => s"$c neither positive nor negative"
    }
}

Il est important de vous assurer que vos gardes ne créent pas une correspondance non exhaustive (le compilateur ne détectera souvent pas cela):

def f(x: Option[Int]) = x match {
    case Some(i) if i % 2 == 0 => doSomething(i)
    case None    => doSomethingIfNone
}

Cela jette un MatchError sur les nombres impairs. Vous devez soit prendre en compte tous les cas, soit utiliser une casse générique:

def f(x: Option[Int]) = x match {
    case Some(i) if i % 2 == 0 => doSomething(i)
    case _ => doSomethingIfNoneOrOdd
}

Correspondance de motifs avec les classes de cas

Chaque classe de cas définit un extracteur qui peut être utilisé pour capturer les membres de la classe de cas lorsque la correspondance de modèle:

case class Student(name: String, email: String)

def matchStudent1(student: Student): String = student match {
    case Student(name, email) => s"$name has the following email: $email" // extract name and email
}

Toutes les règles normales de correspondance de modèle s'appliquent - vous pouvez utiliser des gardes et des expressions constantes pour contrôler la correspondance:

def matchStudent2(student: Student): String = student match {
    case Student("Paul", _) => "Matched Paul" // Only match students named Paul, ignore email
    case Student(name, _) if name == "Paul" => "Matched Paul" // Use a guard to match students named Paul, ignore email
    case s if s.name == "Paul" => "Matched Paul" // Don't use extractor; use a guard to match students named Paul, ignore email
    case Student("Joe", email) => s"Joe has email $email" // Match students named Joe, capture their email
    case Student(name, email) if name == "Joe" => s"Joe has email $email" // use a guard to match students named Joe, capture their email
    case Student(name, email) => s"$name has email $email." // Match all students, capture name and email 
}

Correspondance sur une option

Si vous correspondez à un type d’ option :

def f(x: Option[Int]) = x match {
    case Some(i) => doSomething(i)
    case None    => doSomethingIfNone
}

C'est fonctionnellement équivalent à utiliser fold , ou map / getOrElse :

def g(x: Option[Int]) = x.fold(doSomethingIfNone)(doSomething)
def h(x: Option[Int]) = x.map(doSomething).getOrElse(doSomethingIfNone)

Traitements scellés assortis

Lorsque le motif correspondant à un objet dont le type est un trait scellé, Scala vérifiera au moment de la compilation que tous les cas sont «appariés de manière exhaustive»:

sealed trait Shape
case class Square(height: Int, width: Int) extends Shape
case class Circle(radius: Int) extends Shape
case object Point extends Shape


def matchShape(shape: Shape): String = shape match {
    case Square(height, width) => "It's a square"
    case Circle(radius)        => "It's a circle"
    //no case for Point because it would cause a compiler warning.
}

Si une nouvelle case class pour Shape est ajoutée ultérieurement, toutes les instructions de match sur Shape commenceront à émettre un avertissement de compilation. Cela facilite le refactoring complet: le compilateur alertera le développeur de tout le code qui doit être mis à jour.

Correspondance de motifs avec Regex

val emailRegex: Regex = "(.+)@(.+)\\.(.+)".r

"[email protected]" match {
  case emailRegex(userName, domain, topDomain) => println(s"Hi $userName from $domain")
  case _ => println(s"This is not a valid email.")
}

Dans cet exemple, l'expression régulière tente de correspondre à l'adresse électronique fournie. Si c'est le cas, le nom d' userName et le domain sont extraits et imprimés. topDomain est également extrait, mais rien n'est fait dans cet exemple. L'appel de .r sur un String str est équivalent à new Regex(str) . La fonction r est disponible via une conversion implicite .

Reliure de motif (@)

Le signe @ lie une variable à un nom lors d'une correspondance de modèle. La variable liée peut être soit l’objet correspondant entier, soit une partie de l’objet correspondant:

sealed trait Shape
case class Rectangle(height: Int, width: Int) extends Shape
case class Circle(radius: Int) extends Shape
case object Point extends Shape

(Circle(5): Shape) match {
  case Rectangle(h, w) => s"rectangle, $h x $w."
  case Circle(r) if r > 9 => s"large circle"
  case c @ Circle(_) => s"small circle: ${c.radius}"  // Whole matched object is bound to c
  case Point => "point"
}

> res0: String = small circle: 5

L'identifiant lié peut être utilisé dans des filtres conditionnels. Ainsi:

case Circle(r) if r > 9 => s"large circle"

peut être écrit comme:

case c @ Circle(_) if c.radius > 9 => s"large circle"

Le nom ne peut être lié qu'à une partie du motif correspondant:

Seq(Some(1), Some(2), None) match {
  // Only the first element of the matched sequence is bound to the name 'c'
  case Seq(c @ Some(1), _*) => head
  case _ => None
}

> res0: Option[Int] = Some(1)

Types de correspondance

La correspondance de modèle peut également être utilisée pour vérifier le type d'une instance, plutôt que d'utiliser isInstanceOf[B] :

val anyRef: AnyRef = ""
                                                  
anyRef match {
  case _: Number       => "It is a number"
  case _: String       => "It is a string"
  case _: CharSequence => "It is a char sequence"
}
//> res0: String = It is a string

L'ordre des cas est important:

anyRef match {
  case _: Number       => "It is a number"
  case _: CharSequence => "It is a char sequence"
  case _: String       => "It is a string"
}
//> res1: String = It is a char sequence

De cette manière, il est similaire à une instruction "switch" classique, sans la fonctionnalité fall-through. Cependant, vous pouvez également créer des correspondances et extraire des valeurs du type en question. Par exemple:

case class Foo(s: String)
case class Bar(s: String)
case class Woo(s: String, i: Int)

def matcher(g: Any):String = {
  g match {
    case Bar(s) => s + " is classy!" 
    case Foo(_) => "Someone is wicked smart!"
    case Woo(s, _) => s + " is adventerous!"
    case _ => "What are we talking about?"
  }
}

print(matcher(Foo("Diana")))  // prints 'Diana is classy!'
print(matcher(Bar("Hadas")))  // prints 'Someone is wicked smart!'
print(matcher(Woo("Beth", 27)))   // prints 'Beth is adventerous!'
print(matcher(Option("Katie")))  // prints 'What are we talking about?'

Notez que dans les cas Foo et Woo , nous utilisons le trait de soulignement ( _ ) pour "correspondre à une variable non liée". C'est-à-dire que la valeur (dans ce cas, Hadas et 27 , respectivement) n'est pas liée à un nom et n'est donc pas disponible dans le gestionnaire pour ce cas. Ceci est un raccourci utile pour faire correspondre «n'importe quelle valeur» sans se soucier de sa valeur.

Correspondance de modèle compilée en tant que commutateur de table ou commutateur de recherche

L'annotation @switch indique au compilateur que l'instruction de match peut être remplacée par une seule instruction de tableswitch au niveau du bytecode. Il s'agit d'une optimisation mineure qui peut supprimer les comparaisons inutiles et les charges variables pendant l'exécution.

L'annotation @switch ne fonctionne que pour les correspondances avec les constantes littérales et les identificateurs de final val . Si la correspondance de modèle ne peut pas être compilée en tant que tableswitch / lookupswitch , le compilateur lookupswitch un avertissement.

import annotation.switch

def suffix(i: Int) = (i: @switch) match {
  case 1 => "st"
  case 2 => "nd"
  case 3 => "rd"
  case _ => "th"
}

Les résultats sont identiques à ceux d'un modèle normal:

scala> suffix(2)
res1: String = "2nd"

scala> suffix(4)
res2: String = "4th"

De la documentation Scala (2.8+) - @switch :

Une annotation à appliquer à une expression de correspondance. S'il est présent, le compilateur vérifiera que la correspondance a été compilée avec un commutateur de tables ou un commutateur de recherche et génère une erreur si elle compile au lieu de cela en une série d'expressions conditionnelles.

De la spécification Java:

  • switch : "Accéder à la table de saut par index et saut"
  • lookupswitch : "Accéder à la table de saut par match de la clé et sauter"

Faire correspondre plusieurs modèles à la fois

Le | peut être utilisé pour avoir une seule déclaration de cas avec plusieurs entrées pour obtenir le même résultat:

def f(str: String): String = str match {
  case "foo" | "bar" => "Matched!"
  case _ => "No match."
}

f("foo")  // res0: String = Matched!
f("bar")  // res1: String = Matched!
f("fubar")  // res2: String = No match.

Notez que, bien que les valeurs correspondent de cette manière, la correspondance des types suivants posera des problèmes:

sealed class FooBar
case class Foo(s: String) extends FooBar
case class Bar(s: String) extends FooBar

val d = Foo("Diana")
val h = Bar("Hadas")

// This matcher WILL NOT work.
def matcher(g: FooBar):String = {
  g match {
    case Foo(s) | Bar(s) => print(s)  // Won't work: s cannot be resolved
    case Foo(_) | Bar(_) => _         // Won't work: _ is an unbound placeholder
    case _ => "Could not match"
  }
}

Si dans le dernier cas (avec _ ) vous n'avez pas besoin de la valeur de la variable non liée et que vous voulez juste faire autre chose, vous allez bien:

def matcher(g: FooBar):String = {
  g match {
    case Foo(_) | Bar(_) => "Is either Foo or Bar."  // Works fine
    case _ => "Could not match"
  }
}

Sinon, il vous reste à diviser vos affaires:

def matcher(g: FooBar):String = {
  g match {
    case Foo(s) => s 
    case Bar(s) => s
    case _ => "Could not match"
  }
}

Correspondance des motifs sur les tuples

Étant donné la List suivante de tuples:

val pastries = List(("Chocolate Cupcake", 2.50), 
                    ("Vanilla Cupcake", 2.25),
                    ("Plain Muffin", 3.25))

La correspondance de modèle peut être utilisée pour gérer chaque élément différemment:

pastries foreach { pastry =>
  pastry match {
    case ("Plain Muffin", price) => println(s"Buying muffin for $price")
    case p if p._1 contains "Cupcake" => println(s"Buying cupcake for ${p._2}")
    case _ => println("We don't sell that pastry")
  }
}

Le premier cas montre comment faire correspondre une chaîne spécifique et obtenir le prix correspondant. Le deuxième cas montre une utilisation de l' extraction if et tuple pour correspondre aux éléments du tuple.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow