Suche…


Zusammensetzung

Teilfunktionen werden häufig verwendet, um eine Gesamtfunktion in Teilen zu definieren:

sealed trait SuperType
case object A extends SuperType
case object B extends SuperType
case object C extends SuperType

val pfA: PartialFunction[SuperType, Int] = {
  case A => 5
}

val pfB: PartialFunction[SuperType, Int] = {
  case B => 10
}

val input: Seq[SuperType] = Seq(A, B, C)

input.map(pfA orElse pfB orElse {
  case _ => 15
}) // Seq(5, 10, 15)

In dieser Verwendung werden die Teilfunktionen in der Reihenfolge der Verkettung mit der orElse Methode versucht. Normalerweise wird eine abschließende Teilfunktion bereitgestellt, die alle verbleibenden Fälle erfüllt. Insgesamt wirkt die Kombination dieser Funktionen als Gesamtfunktion.

Dieses Muster wird normalerweise verwendet, um Bedenken zu trennen, bei denen eine Funktion einen Dispatcher effektiv für unterschiedliche Codepfade einsetzen kann. Dies ist zum Beispiel bei der Empfangsmethode eines Akka-Schauspielers üblich .

Verwendung mit "Collect"

Während Teilfunktionen oft als bequeme Syntax für Gesamtfunktionen verwendet werden, indem eine abschließende Platzhalterübereinstimmung ( case _ ) eingefügt wird, ist bei einigen Methoden deren Parteilichkeit der Schlüssel. Ein sehr verbreitetes Beispiel in idiomatischer Scala ist die collect Methode, die in der Scala-Sammlungsbibliothek definiert wird. Teilfunktionen ermöglichen hier die gemeinsamen Funktionen der Untersuchung der Elemente einer Sammlung, um diese abzubilden und / oder zu filtern, damit sie in einer kompakten Syntax vorkommen.

Beispiel 1

Angenommen, wir haben eine Quadratwurzelfunktion, die als Teilfunktion definiert ist:

val sqRoot:PartialFunction[Double,Double] = { case n if n > 0 => math.sqrt(n) }

Wir können es mit dem collect Combinator aufrufen:

List(-1.1,2.2,3.3,0).collect(sqRoot)

effektiv die gleiche Operation ausführen wie:

List(-1.1,2.2,3.3,0).filter(sqRoot.isDefinedAt).map(sqRoot)

Beispiel 2

sealed trait SuperType // `sealed` modifier allows inheritance within current build-unit only
case class A(value: Int) extends SuperType
case class B(text: String) extends SuperType
case object C extends SuperType

val input: Seq[SuperType] = Seq(A(5), B("hello"), C, A(25), B(""))

input.collect {
  case A(value) if value < 10   => value.toString
  case B(text) if text.nonEmpty => text
} // Seq("5", "hello")

Im obigen Beispiel sind einige Punkte zu beachten:

  • Die linke Seite jedes Mustermatches wählt effektiv Elemente aus, die verarbeitet und in die Ausgabe aufgenommen werden sollen. Jeder Wert, der keinen passenden case wird einfach weggelassen.
  • Die rechte Seite definiert die anwendungsspezifische Verarbeitung.
  • Pattern Matching bindet Variable zur Verwendung in Guard-Anweisungen ( if Klauseln) und auf der rechten Seite.

Grundlegende Syntax

Scala verfügt über eine spezielle Funktion , die als Teilfunktion bezeichnet wird und die normalen Funktionen erweitert. PartialFunction bedeutet, dass eine PartialFunction Instanz überall dort eingesetzt werden kann, wo Function1 erwartet wird. Teilfunktionen können anonym mit der case der Mustererkennung verwendeten Fallsyntax definiert werden :

val pf: PartialFunction[Boolean, Int] = {
  case true => 7
}

pf.isDefinedAt(true) // returns true
pf(true) // returns 7

pf.isDefinedAt(false) // returns false
pf(false) // throws scala.MatchError: false (of class java.lang.Boolean)

Wie im Beispiel zu sehen ist, muss eine Teilfunktion nicht über die gesamte Domäne des ersten Parameters definiert werden. Eine Standard-Instanz von Function1 wird als total angenommen , was bedeutet, dass sie für jedes mögliche Argument definiert ist.

Verwendung als Gesamtfunktion

Teilfunktionen sind in idiomatischen Scala sehr häufig. Sie werden häufig für ihre praktische case , um die Gesamtfunktionen über die Merkmale zu definieren:

sealed trait SuperType // `sealed` modifier allows inheritance within current build-unit only
case object A extends SuperType
case object B extends SuperType
case object C extends SuperType

val input: Seq[SuperType] = Seq(A, B, C)

input.map {
  case A => 5
  case _ => 10
} // Seq(5, 10, 10)

Dadurch wird die zusätzliche Syntax einer match in einer normalen anonymen Funktion gespeichert. Vergleichen Sie:

input.map { item => 
  item match {
    case A => 5
    case _ => 10
  }
} // Seq(5, 10, 10)

Sie wird auch häufig verwendet, um eine Parameterzerlegung mit Hilfe von Pattern Matching durchzuführen, wenn ein Tupel oder eine Fallklasse an eine Funktion übergeben wird:

val input = Seq("A" -> 1, "B" -> 2, "C" -> 3)

input.map { case (a, i) =>
   a + i.toString
} // Seq("A1", "B2", "C3")

Verwendung zum Extrahieren von Tupeln in einer Kartenfunktion

Diese drei Kartenfunktionen sind gleichwertig. Verwenden Sie also die Variante, die Ihr Team für lesbar hält.

val numberNames = Map(1 -> "One", 2 -> "Two", 3 -> "Three")

// 1. No extraction
numberNames.map(it => s"${it._1} is written ${it._2}" )

// 2. Extraction within a normal function
numberNames.map(it => {
    val (number, name) = it
    s"$number is written $name"
})

// 3. Extraction via a partial function (note the brackets in the parentheses)
numberNames.map({ case (number, name) => s"$number is written $name" })

Die Teilfunktion muss mit allen Eingaben übereinstimmen : Jeder Fall, der nicht übereinstimmt, löst zur Laufzeit eine Ausnahme aus.



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