Scala Language
Delvisa funktioner
Sök…
Sammansättning
Delfunktioner används ofta för att definiera en totalfunktion i delar:
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)
Vid denna användning försöks de partiella funktionerna i ordningsföljd för sammankoppling med orElse
metoden. Vanligtvis tillhandahålls en slutlig partiell funktion som matchar alla återstående fall. Sammantaget fungerar kombinationen av dessa funktioner som en total funktion.
Detta mönster används vanligtvis för att separera problem där en funktion effektivt kan verka en avsändare för olika kodvägar. Detta är vanligtvis till exempel i mottagningsmetoden för en Akka-skådespelare .
Användning med "samla"
Medan partiell funktion ofta används som bekväm syntax för totala funktioner, genom att inkludera en slutlig jokerteckenmatchning ( case _
), är vissa partier i vissa metoder viktiga. Ett mycket vanligt exempel på idiomatisk Scala är collect
metod, som definieras i Scala samlingar biblioteket. Här tillåter delfunktioner de vanliga funktionerna för att undersöka elementen i en samling för att kartlägga och / eller filtrera dem i en kompakt syntax.
Exempel 1
Förutsatt att vi har en kvadratrotfunktion definierad som partiell funktion:
val sqRoot:PartialFunction[Double,Double] = { case n if n > 0 => math.sqrt(n) }
Vi kan åberopa det med collect
:
List(-1.1,2.2,3.3,0).collect(sqRoot)
effektivt utföra samma operation som:
List(-1.1,2.2,3.3,0).filter(sqRoot.isDefinedAt).map(sqRoot)
Exempel 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")
Det finns flera saker att notera i exemplet ovan:
- Vänster sida av varje mönster matchar effektivt element som ska bearbetas och inkluderas i utgången. Allt värde som inte har ett matchande
case
utelämnas helt enkelt. - Den högra sidan definierar den fallspecifika behandling som ska tillämpas.
- Mönstermatchning binder variabel för användning i skyddsuttalanden (
if
klausulerna) och höger sida.
Grundläggande syntax
Scala har en speciell typ av funktion som kallas en partiell funktion , som utökar normala funktioner - vilket innebär att en PartialFunction
instans kan användas varhelst Function1
förväntas. Partiella funktioner kan definieras anonymt med hjälp av case
syntax som också används i mönstermatchning :
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)
Som framgår av exemplet behöver en partiell funktion inte definieras över hela domänen för dess första parameter. En standardinställning av Function1
antas vara total , vilket innebär att den definieras för alla möjliga argument.
Användning som en total funktion
Partiella funktioner är mycket vanliga i idiomatiska Scala. De används ofta för sin bekväm case
baserad syntax för att definiera den totala funktioner över egenskaper :
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)
Detta sparar ytterligare syntaxen för en match
uttalande i en vanlig anonym funktion. Jämföra:
input.map { item =>
item match {
case A => 5
case _ => 10
}
} // Seq(5, 10, 10)
Det används också ofta för att utföra en nedbrytning av parametrar med hjälp av mönstermatchning, när en tupel eller en fallklass överförs till en funktion:
val input = Seq("A" -> 1, "B" -> 2, "C" -> 3)
input.map { case (a, i) =>
a + i.toString
} // Seq("A1", "B2", "C3")
Användning för att extrahera tuples i en kartfunktion
Dessa tre kartfunktioner är likvärdiga, så använd den variation som ditt team tycker är mest läsbart.
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" })
Partifunktionen måste matcha alla inmatningar : alla fall som inte matchar kommer att kasta ett undantag vid körning.