Recherche…


Syntaxe

  • extracteur de val (extractValue1, _ / * seconde valeur extraite ignorée * /) = valeurToBeExtracter
  • match valueToBeExtracted {extracteur de cas (extractValue1, _) => ???}
  • val (tuple1, tuple2, tuple3) = tupleWith3Elements
  • objet Foo {def unapply (foo: Foo): Option [String] = Some (foo.x); }

Extracteurs de tuple

x et y sont extraits du tuple:

val (x, y) = (1337, 42)
// x: Int = 1337
// y: Int = 42

Pour ignorer une valeur, utilisez _ :

val (_, y: Int) = (1337, 42)
// y: Int = 42

Déballer un extracteur:

val myTuple = (1337, 42)
myTuple._1  // res0: Int = 1337
myTuple._2  // res1: Int = 42

Notez que tuples ont une longueur maximale de 22 ans , et donc ._1 par ._22 fonctionnera ( en supposant que le tuple est au moins de cette taille).

Les extracteurs de tuples peuvent être utilisés pour fournir des arguments symboliques aux fonctions littérales:

val persons = List("A." -> "Lovelace", "G." -> "Hopper")
val names = List("Lovelace, A.", "Hopper, G.")

assert {
  names ==
    (persons map { name =>
      s"${name._2}, ${name._1}"
    })
}

assert {
  names ==
    (persons map { case (given, surname) =>
      s"$surname, $given"
    })
}

Extracteurs de classe de cas

Une classe de cas est une classe avec beaucoup de code standard standard inclus automatiquement. Un des avantages de ceci est que Scala facilite l'utilisation des extracteurs avec des classes de cas.

case class Person(name: String, age: Int)  // Define the case class
val p = Person("Paola", 42)  // Instantiate a value with the case class type

val Person(n, a) = p  // Extract values n and a
// n: String = Paola
// a: Int = 42

A ce stade, n et a sont tous deux val s dans le programme et sont accessibles en tant que tels: on dit qu'ils ont été «extraits» de p. Continue:

val p2 = Person("Angela", 1337)

val List(Person(n1, a1), Person(_, a2)) = List(p, p2)
// n1: String = Paola
// a1: Int = 42
// a2: Int = 1337

Nous voyons ici deux choses importantes:

  • L'extraction peut avoir lieu à des niveaux «profonds»: les propriétés des objets imbriqués peuvent être extraites.
  • Tous les éléments ne doivent pas être extraits. Le caractère générique _ indique que cette valeur particulière peut être n'importe quoi et est ignorée. Aucun val est créé.

En particulier, cela peut faciliter l’adaptation des collections:

val ls = List(p1, p2, p3)  // List of Person objects
ls.map(person => person match {
  case Person(n, a) => println("%s is %d years old".format(n, a))
})

Ici, nous avons du code qui utilise l'extracteur pour vérifier explicitement que cette person est un objet Person et extraire immédiatement les variables qui nous intéressent: n et a .

Unapply - Extracteurs personnalisés

Une extraction personnalisée peut être écrite en implémentant la méthode unapply et en renvoyant une valeur de type Option :

class Foo(val x: String)

object Foo {
  def unapply(foo: Foo): Option[String] = Some(foo.x)
}

new Foo("42") match {
  case Foo(x) => x
}
// "42"

Le type de retour d' unapply peut être différent de Option , à condition que le type renvoyé fournisse des méthodes get et isEmpty . Dans cet exemple, Bar est défini avec ces méthodes, et unapply renvoie une instance de Bar :

class Bar(val x: String) {
  def get = x
  def isEmpty = false
}

object Bar {
  def unapply(bar: Bar): Bar = bar
}

new Bar("1337") match {
  case Bar(x) => x
}
// "1337"

Le type de retour de unapply peut aussi être un Boolean , ce qui est un cas particulier qui ne répond pas aux exigences de get et isEmpty ci-dessus. Cependant, notez dans cet exemple que DivisibleByTwo est un objet, pas une classe, et ne prend pas de paramètre (et donc ce paramètre ne peut pas être lié):

object DivisibleByTwo {
  def unapply(num: Int): Boolean = num % 2 == 0
}

4 match {                        
  case DivisibleByTwo() => "yes" 
  case _ => "no"
}
// yes

3 match {
  case DivisibleByTwo() => "yes"
  case _ => "no"
}
// no

Rappelez-vous que unapply va pas dans l'objet compagnon d'une classe, pas dans la classe. L'exemple ci-dessus sera clair si vous comprenez cette distinction.

Notation Infix Extractor

Si une classe de cas a exactement deux valeurs, son extracteur peut être utilisé en notation infixe.

case class Pair(a: String, b: String)
val p: Pair = Pair("hello", "world")
val x Pair y = p
//x: String = hello
//y: String = world

Tout extracteur qui retourne un 2-tuple peut fonctionner de cette façon.

object Foo {
    def unapply(s: String): Option[(Int, Int)] = Some((s.length, 5))
}
val a Foo b = "hello world!"
//a: Int = 12
//b: Int = 5

Extracteurs de regex

Une expression régulière avec des parties groupées peut être utilisée comme extracteur:

scala> val address = """(.+):(\d+)""".r
address: scala.util.matching.Regex = (.+):(\d+)

scala> val address(host, port) = "some.domain.org:8080"
host: String = some.domain.org
port: String = 8080

Notez que si elle ne correspond pas, une MatchError sera lancée à l'exécution:

scala> val address(host, port) = "something not a host and port"
scala.MatchError: something not a host and port (of class java.lang.String)

Extracteurs transformateurs

Le comportement des extracteurs peut être utilisé pour obtenir des valeurs arbitraires à partir de leurs entrées. Cela peut être utile dans les scénarios où vous souhaitez pouvoir agir sur les résultats d'une transformation en cas de réussite de la transformation.

Prenons l'exemple des différents formats de noms d'utilisateur utilisables dans un environnement Windows :

object UserPrincipalName {
  def unapply(str: String): Option[(String, String)] = str.split('@') match {
    case Array(u, d) if u.length > 0 && d.length > 0 => Some((u, d))
    case _ => None
  }        
}

object DownLevelLogonName {
  def unapply(str: String): Option[(String, String)] = str.split('\\') match {
    case Array(d, u) if u.length > 0 && d.length > 0 => Some((d, u))
    case _ => None
  }
}

def getDomain(str: String): Option[String] = str match {
  case UserPrincipalName(_, domain) => Some(domain)
  case DownLevelLogonName(domain, _) => Some(domain)
  case _ => None
}

En fait, il est possible de créer un extracteur présentant les deux comportements en élargissant les types qu'il peut correspondre:

object UserPrincipalName {
  def unapply(obj: Any): Option[(String, String)] = obj match {
    case upn: UserPrincipalName => Some((upn.username, upn.domain))
    case str: String => str.split('@') match {
      case Array(u, d) if u.length > 0 && d.length > 0 => Some((u, d))
      case _ => None
    }
    case _ => None
  }        
}

En général, les extracteurs sont simplement une reformulation pratique du modèle Option , appliqué aux méthodes avec des noms tels que tryParse :

UserPrincipalName.unapply("user@domain") match {
  case Some((u, d)) => ???
  case None => ???
}


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