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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow