Scala Language
Extractores
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únval
.
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 => ???
}