Scala Language
Extraktoren
Suche…
Syntax
- val-extractor (extrahierterWert1, _ / * ignorierter zweiter extrahierter Wert * /) = valueToBeExtracted
- valueToBeExtracted match {case extractor (extrahierterWert1, _) => ???}
- val (tuple1, tuple2, tuple3) = tupleWith3Elements
- object Foo {def unapply (foo: Foo): Option [String] = Some (foo.x); }
Tupel-Extraktoren
x
und y
werden aus dem Tupel extrahiert:
val (x, y) = (1337, 42) // x: Int = 1337 // y: Int = 42
Um einen Wert zu ignorieren, verwenden Sie _
:
val (_, y: Int) = (1337, 42) // y: Int = 42
So entpacken Sie einen Extraktor:
val myTuple = (1337, 42) myTuple._1 // res0: Int = 1337 myTuple._2 // res1: Int = 42
Beachten Sie, dass Tupel eine maximale Länge von 22 haben und somit ._1
bis ._22
funktionieren (vorausgesetzt, das Tupel hat mindestens diese Größe).
Tupel-Extraktoren können verwendet werden, um symbolische Argumente für wörtliche Funktionen bereitzustellen:
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 Extraktoren
Eine Fallklasse ist eine Klasse mit einer Menge Standardcode, der automatisch enthalten ist. Ein Vorteil davon ist, dass Scala die Verwendung von Extraktoren mit Fallklassen vereinfacht.
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
Zu diesem Zeitpunkt sind sowohl n
als auch a
val
im Programm, auf die als solche zugegriffen werden kann: Sie sollen aus p "extrahiert" worden sein. Auch weiterhin:
val p2 = Person("Angela", 1337)
val List(Person(n1, a1), Person(_, a2)) = List(p, p2)
// n1: String = Paola
// a1: Int = 42
// a2: Int = 1337
Hier sehen wir zwei wichtige Dinge:
- Die Extraktion kann auf tiefen Ebenen erfolgen: Eigenschaften von verschachtelten Objekten können extrahiert werden.
- Nicht alle Elemente müssen extrahiert werden. Das Platzhalterzeichen
_
gibt an, dass dieser bestimmte Wert alles sein kann und ignoriert wird. Es wird keinval
erstellt.
Dies kann insbesondere das Abgleichen von Sammlungen erleichtern:
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))
})
Hier haben wir Code, der den Extraktor verwendet, um explizit zu prüfen, ob person
ein Person
ist, und sofort die Variablen herauszuholen, die uns wichtig sind: n
und a
.
Unangenehm - benutzerdefinierte Extraktoren
Eine benutzerdefinierte Extraktion kann geschrieben werden, indem die unapply
Methode implementiert und ein Wert vom Typ 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"
Der Rückgabetyp von unapply
kann etwas anderes als Option
, sofern der zurückgegebene Typ die Methoden get
und isEmpty
. In diesem Beispiel wird Bar
mit diesen Methoden definiert und gibt eine Instanz von Bar
unapply
zurück:
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"
Der Rückgabetyp von unapply
kann auch ein Boolean
unapply
ist ein Sonderfall, der get
isEmpty
Anforderungen von get
und isEmpty
nicht enthält. Beachten Sie jedoch in diesem Beispiel, dass DivisibleByTwo
ein Objekt und keine Klasse ist und keinen Parameter verwendet (daher kann dieser Parameter nicht gebunden werden):
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
Denken unapply
daran, dass das Begleitobjekt einer Klasse unapply
nicht in die Klasse geht. Das obige Beispiel ist klar, wenn Sie diesen Unterschied verstehen.
Extraktor-Infix-Notation
Wenn eine Fallklasse genau zwei Werte hat, kann der Extraktor in Infix-Notation verwendet werden.
case class Pair(a: String, b: String)
val p: Pair = Pair("hello", "world")
val x Pair y = p
//x: String = hello
//y: String = world
Jeder Extraktor, der ein 2-Tupel zurückgibt, kann auf diese Weise arbeiten.
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-Extraktoren
Ein regulärer Ausdruck mit gruppierten Teilen kann als Extraktor verwendet werden:
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
Beachten Sie, dass ein MatchError
zur Laufzeit MatchError
wird, wenn er nicht übereinstimmt:
scala> val address(host, port) = "something not a host and port"
scala.MatchError: something not a host and port (of class java.lang.String)
Transformative Extraktoren
Das Verhalten des Extraktors kann verwendet werden, um aus ihrer Eingabe beliebige Werte abzuleiten. Dies kann in Szenarien hilfreich sein, in denen Sie die Ergebnisse einer Umwandlung für den Fall berücksichtigen können, dass die Umwandlung erfolgreich ist.
Betrachten Sie als Beispiel die verschiedenen Benutzernamenformate, die in einer Windows-Umgebung verwendet werden können :
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
}
Tatsächlich ist es möglich, einen Extraktor mit beiden Verhaltensweisen zu erstellen, indem die Typen erweitert werden, auf die er passen kann:
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
}
}
Im Allgemeinen sind Extraktoren einfach eine bequeme Neuformulierung des Option
, die auf Methoden mit Namen wie tryParse
:
UserPrincipalName.unapply("user@domain") match {
case Some((u, d)) => ???
case None => ???
}