Ricerca…


Sintassi

  • val extractor (extractedValue1, _ / * secondo valore estratto estrapolato * /) = valueToBeExtracted
  • valueToBeStruttura abbinata {case extractor (extractedValue1, _) => ???}
  • val (tuple1, tuple2, tuple3) = tupleWith3Elements
  • oggetto Foo {def unapply (foo: Foo): Option [String] = Some (foo.x); }

Estrattori a tupla

x e y sono estratti dalla tupla:

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

Per ignorare un valore, utilizzare _ :

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

Per decomprimere un estrattore:

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

Nota che le tuple hanno una lunghezza massima di 22, e quindi ._1 da ._22 a ._22 (supponendo che la tupla abbia almeno quella dimensione).

Gli estrattori di tupla possono essere usati per fornire argomenti simbolici per le funzioni letterali:

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

Estrattori Case Class

Una classe di casi è una classe con un codice standard di codice standard che è incluso automaticamente. Un vantaggio di questo è che Scala semplifica l'uso di estrattori con classi di casi.

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

In questo frangente, sia n che a sono val nel programma e sono accessibili come tali: si dice che siano stati "estratti" da p. continuando:

val p2 = Person("Angela", 1337)

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

Qui vediamo due cose importanti:

  • L'estrazione può avvenire a livelli "profondi": è possibile estrarre le proprietà degli oggetti nidificati.
  • Non tutti gli elementi devono essere estratti. Il jolly _ carattere indica che quel particolare valore può essere qualsiasi cosa, e viene ignorato. Nessun val è stato creato.

In particolare, questo può semplificare la corrispondenza tra le raccolte:

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

Qui, abbiamo un codice che usa l'extractor per verificare esplicitamente che la person sia un oggetto Person e immediatamente estrae le variabili che ci interessano: n e a .

Unapply - Estrattori personalizzati

È possibile scrivere un'estrazione personalizzata implementando il metodo unapply e restituendo un valore di tipo 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"

Il tipo restituito di unapply potrebbe essere qualcosa di diverso da Option , a condizione che il tipo restituito fornisca i metodi get e isEmpty . In questo esempio, la Bar viene definita con tali metodi e riappare in modo unapply un'istanza di 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"

Il tipo restituito di unapply può anche essere un Boolean , che è un caso speciale che non ha i requisiti get ed isEmpty sopra. Tuttavia, si noti in questo esempio che DivisibleByTwo è un oggetto, non una classe, e non accetta un parametro (e quindi tale parametro non può essere associato):

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

Ricorda che unapply va nell'oggetto compagno di una classe, non nella classe. L'esempio sopra sarà chiaro se capisci questa distinzione.

Notazione Infix dell'estrattore

Se una classe del caso ha esattamente due valori, il suo estrattore può essere utilizzato nella notazione infisso.

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

Qualsiasi estrattore che restituisce una tupla da 2 può funzionare in questo modo.

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

Estrattori Regex

Un'espressione regolare con parti raggruppate può essere utilizzata come estrattore:

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

Si noti che quando non è abbinato, viene MatchError un MatchError in fase di runtime:

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

Estrattori trasformativi

Il comportamento dell'estrattore può essere utilizzato per ricavare valori arbitrari dal loro input. Ciò può essere utile negli scenari in cui si desidera essere in grado di agire sui risultati di una trasformazione nel caso in cui la trasformazione abbia esito positivo.

Considerare come esempio i vari formati di nome utente utilizzabili in un ambiente 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
}

In effetti è possibile creare un estrattore che mostri entrambi i comportamenti allargando i tipi che può abbinare:

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

In generale, gli estrattori sono semplicemente una conveniente riformulazione del pattern Option , applicato ai metodi con nomi come tryParse :

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


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow