Scala Language
Teilfunktionen
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.