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 kein val 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 => ???
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow