Scala Language
Extraktorer
Sök…
Syntax
- val extraherare (extraheradValue1, _ / * ignorerat andra extraherade värdet * /) = valueToBeExtracted
- valueToBeExtracted match {case extractor (extractedValue1, _) => ???}
- val (tuple1, tuple2, tuple3) = tupleWith3Elements
- objekt Foo {def unapply (foo: Foo): Alternativ [String] = Vissa (foo.x); }
Tuple Extractors
x
och y
extraheras från tupeln:
val (x, y) = (1337, 42) // x: Int = 1337 // y: Int = 42
För att ignorera ett värde använd _
:
val (_, y: Int) = (1337, 42) // y: Int = 42
Så här packar du upp en extraktor:
val myTuple = (1337, 42) myTuple._1 // res0: Int = 1337 myTuple._2 // res1: Int = 42
Observera att tuplen har en maximal längd på 22 och att ._1
och med ._22
fungerar (förutsatt att tupeln är åtminstone den storleken).
Tuple-extraktorer kan användas för att tillhandahålla symboliska argument för bokstavliga funktioner:
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
En fallklass är en klass med många standardpannkod som automatiskt ingår. En fördel med detta är att Scala gör det enkelt att använda extraktorer med fallklasser.
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
Vid denna tidpunkt är både n
och a
val
i programmet och kan nås som sådana: de sägs ha "extraherats" från p. Fortlöpande:
val p2 = Person("Angela", 1337)
val List(Person(n1, a1), Person(_, a2)) = List(p, p2)
// n1: String = Paola
// a1: Int = 42
// a2: Int = 1337
Här ser vi två viktiga saker:
- Extraktion kan ske på "djupa" nivåer: egenskaper hos kapslade objekt kan extraheras.
- Inte alla delar behöver extraheras. Jokerteckenet
_
indikerar att det specifika värdet kan vara vad som helst och ignoreras. Ingenval
skapas.
I synnerhet kan detta göra matchning över samlingar lätt:
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))
})
Här har vi kod som använder extraheraren för att uttryckligen kontrollera att person
är ett Person
och dra omedelbart ut variablerna som vi bryr oss om: n
och a
.
Oavsett - anpassade extraktorer
En anpassad extraktion kan skrivas genom att implementera metoden unapply
och returnera ett värde av typen 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"
unapply
för unapply
kan vara något annat än Option
, förutsatt att den returnerade typen ger get
och isEmpty
metoder. I detta exempel, Bar
definieras med dessa metoder och unapply
avkastning en instans av 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"
Återlämnandet av unapply
kan också vara en Boolean
, vilket är ett speciellt fall som inte bär get
och är isEmpty
krav ovan. Observera dock i detta exempel att DivisibleByTwo
är ett objekt, inte en klass och inte tar en parameter (och därför kan den parametern inte vara bunden):
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
Kom ihåg att unapply
går i unapply
för en klass, inte i klassen. Exemplet ovan kommer att vara tydligt om du förstår denna distinktion.
Extractor Infix notation
Om en fallklass har exakt två värden kan dess extraktor användas i infixnotation.
case class Pair(a: String, b: String)
val p: Pair = Pair("hello", "world")
val x Pair y = p
//x: String = hello
//y: String = world
Varje extraktor som returnerar en 2-tupel kan fungera på detta sätt.
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
Regex Extractors
Ett regelbundet uttryck med grupperade delar kan användas som extraktor:
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
Observera att när det inte matchas kommer en MatchError
att kastas vid körning:
scala> val address(host, port) = "something not a host and port"
scala.MatchError: something not a host and port (of class java.lang.String)
Transformativa extraktorer
Extractorbeteende kan användas för att härleda godtyckliga värden från deras inmatning. Detta kan vara användbart i scenarier där du vill kunna agera på resultatet av en omvandling i händelse av att omvandlingen är framgångsrik.
Betrakta som exempel de olika användarnamnsformaten som kan användas i en Windows-miljö :
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
}
I själva verket är det möjligt att skapa en extraktor som visar båda beteenden genom att bredda de typer som den kan matcha:
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
}
}
Generellt sett är extraktorer helt enkelt en bekväm omformulering av Option
, tillämpat på metoder med namn som tryParse
:
UserPrincipalName.unapply("user@domain") match {
case Some((u, d)) => ???
case None => ???
}