Scala Language
部分関数
サーチ…
組成
パーシャル関数は、多くの場合、パーツ全体の関数を定義するために使用されます。
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)
この使用法では、 orElse
メソッドとの連結の順番で部分関数が試行されます。通常、残りのすべての場合に一致する最終部分関数が提供されます。集合的に、これらの機能の組み合わせは、総合機能として働く。
このパターンは、通常、異種コードパスのディスパッチャを効果的に機能させることができる懸念事項を分離するために使用されます。これは、たとえば、 Akka Actorの受信メソッドでは一般的です。
`collect`での使用法
部分関数は、関数全体の便利な構文としてよく使用されますが、最終的なワイルドカードのマッチ( case _
)を含めることで、一部のメソッドではその部分性が重要です。慣用的なScalaの非常に一般的な例の1つは、Scalaコレクションライブラリで定義されているcollect
メソッドです。ここで、部分的な関数は、コレクションの要素を調べて、それらを1つのコンパクトな構文で現れるようにマップおよび/またはフィルタリングする共通関数を可能にする。
例1
部分関数として定義された平方根関数があると仮定します:
val sqRoot:PartialFunction[Double,Double] = { case n if n > 0 => math.sqrt(n) }
collect
コンビネータでそれを呼び出すことができます:
List(-1.1,2.2,3.3,0).collect(sqRoot)
次のような効果があります。
List(-1.1,2.2,3.3,0).filter(sqRoot.isDefinedAt).map(sqRoot)
例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")
上記の例では、注意すべき点がいくつかあります。
- 各パターンマッチングの左側は、処理する要素を効果的に選択して出力に含める。
case
とcase
が一致しない値は、単に省略されます。 - 右側には、適用するケース固有の処理が定義されています。
- パターンマッチングは、ガードステートメント(
if
句)と右側の変数をバインドします。
基本的な構文
Scalaには、 通常の関数を拡張するpartial関数という特別な型の関数があります。つまり、 Function1
が期待される場所であれば、 PartialFunction
インスタンスを使用できます。 パターンマッチングでも使用されるcase
構文を使用して、部分関数を匿名で定義することができます。
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)
この例に見られるように、部分関数は、その第1パラメータのドメイン全体にわたって定義される必要はない。標準のFunction1
インスタンスは合計であるとみなされます。つまり、使用可能なすべての引数に対して定義されています。
合計関数としての使用法
部分的な関数は、慣用的なScalaでは非常に一般的です。彼らはしばしば彼らの便利のために使用されているcase
にわたり、合計関数を定義するためにベースの構文の特性 :
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)
これにより、通常の無名関数でのmatch
文の追加構文が保存されます。比較:
input.map { item =>
item match {
case A => 5
case _ => 10
}
} // Seq(5, 10, 10)
また、タプルやケースクラスが関数に渡されたときに、パターンマッチングを使用してパラメータ分解を実行するために頻繁に使用されます。
val input = Seq("A" -> 1, "B" -> 2, "C" -> 3)
input.map { case (a, i) =>
a + i.toString
} // Seq("A1", "B2", "C3")
マップ関数でタプルを抽出するための使用法
これらの3つのマップ関数は同等であるため、チームが最も判読しやすいと思われるバリエーションを使用してください。
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" })
部分的な関数はすべての入力と一致する必要があります。一致しない場合は、実行時に例外がスローされます。