Buscar..


Sintaxis

  • extractor de valores (extractValue1, _ / * segundo valor extraído ignorado * /) = valueToBeExtracted
  • valueToBeExtracted match {case extractor (extractValue1, _) => ???}
  • val (tuple1, tuple2, tuple3) = tupleWith3Elements
  • objeto Foo {def unapply (foo: Foo): Opción [String] = Some (foo.x); }

Extractores de tuplas

x e y se extraen de la tupla:

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

Para ignorar un valor usa _ :

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

Para desembalar un extractor:

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

Tenga en cuenta que las tuplas tienen una longitud máxima de 22 y, por lo tanto, de ._1 a ._22 funcionarán (suponiendo que la tupla tenga al menos ese tamaño).

Los extractores de tuplas se pueden usar para proporcionar argumentos simbólicos para funciones literales:

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

Case Class Extractors

Una clase de caso es una clase con una gran cantidad de código estándar incluido automáticamente. Una ventaja de esto es que Scala facilita el uso de extractores con clases de casos.

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

En esta coyuntura, tanto n como a son val en el programa y se puede acceder a ellos como tales: se dice que se han "extraído" de la pág. Continuo:

val p2 = Person("Angela", 1337)

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

Aquí vemos dos cosas importantes:

  • La extracción puede ocurrir en niveles "profundos": se pueden extraer las propiedades de los objetos anidados.
  • No todos los elementos necesitan ser extraídos. El carácter comodín _ indica que ese valor en particular puede ser cualquier cosa, y se ignora. No se crea ningún val .

En particular, esto puede facilitar la comparación entre colecciones:

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

Aquí, tenemos un código que utiliza el extractor para verificar explícitamente que la person es un objeto Person y que saca de inmediato las variables que nos interesan: n y a .

Unapply - Extractores personalizados

Una extracción personalizado puede ser escrito por la aplicación de la unapply método y devolver un valor de 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"

El tipo de unapply de unapply puede ser diferente a la Option , siempre que el tipo devuelto proporcione los métodos get e isEmpty . En este ejemplo, la Bar se define con esos métodos y la unapply devuelve una instancia de la 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"

El tipo de unapply de unapply también puede ser un Boolean , que es un caso especial que no cumple con los requisitos get y isEmpty anteriores. Sin embargo, tenga en cuenta en este ejemplo que DivisibleByTwo es un objeto, no una clase, y no toma un parámetro (y, por lo tanto, ese parámetro no puede ser enlazado):

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

Recuerde que la unapply va en el objeto compañero de una clase, no en la clase. El ejemplo anterior será claro si entiende esta distinción.

Extractor de notación infijo.

Si una clase de caso tiene exactamente dos valores, su extractor puede usarse en notación de infijo.

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

Cualquier extractor que devuelva un 2-tuple puede funcionar de esta manera.

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

Extractores Regex

Una expresión regular con partes agrupadas se puede utilizar como un extractor:

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

Tenga en cuenta que cuando no MatchError se MatchError un MatchError en tiempo de ejecución:

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

Extractores transformadores

El comportamiento del extractor se puede usar para derivar valores arbitrarios de su entrada. Esto puede ser útil en situaciones en las que desea poder actuar sobre los resultados de una transformación en caso de que la transformación sea exitosa.

Considere como ejemplo los diversos formatos de nombre de usuario que se pueden usar en un entorno 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
}

De hecho, es posible crear un extractor que muestre ambos comportamientos ampliando los tipos que puede igualar:

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 general, los extractores son simplemente una reformulación conveniente del patrón de Option , como se aplica a métodos con nombres como tryParse :

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


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow