map
とflatMap
まずは高階関数ですflatMap
。
map
は、その のすべての要素に関数を適用することにより、List
a を newに変換する高階関数でもあります。たとえば、List
List
val l = List(1,2,3)
map
を使用して新しいリストに追加できます
val doubled = l.map(_ * 2) // List(2,4,6)
だから何flatMap
ですか?flatMap
関数が を取得するInt
が を返す場合に便利ですList[Int]
。この場合、この関数を に渡すmap
と、得られるものは aになりますが、代わりList[List[Int]]
に a を取得したい場合もありますList[Int]
。
あなたができることは、flatMap
代わりに使用することです。map
数学的には、最初にflatten
結果に相当しますが、実際には、その逆ではなく、flatten
に基づいて定義された関数になる可能性がありますflatMap
。
技術的な詳細はさておき、flatten
あなたが を持ってList[List[List...[Int]]]
いればflatten
、あなたがそれを手に入れることができることを意味しますList[Int]
。
見ると
def flatten(l: List[Any]): List[Any] = l flatMap {
case ms:List[_] => flatten(ms)
case l => List(l)
}
まず、これが何を意味するのかを知る必要があります。に関数を渡す必要があることは既にわかっていますflatMap
。この場合、渡す関数は
{
case ms:List[_] => flatten(ms)
case l => List(l)
}
最初は関数のように見えませんが、関数です! 実際には、いくつかのニュアンスを持つ関数と見なすことができる部分関数です!
部分関数とは
以下を使用して (ほぼ) 同じ結果を得ることができます。
def flatten(l: List[Any]): List[Any] = l flatMap { _ match {
case ms:List[_] => flatten(ms)
case l => List(l)
}
}
通常の関数と部分関数の違いは、一部の特定の入力に対して部分関数が定義されていない可能性があることです。そのため、コンパイラはキーワードで始まる本体から部分関数を自動的に生成しますcase
(それほど単純ではありませんが、簡単にするためにここでは詳細を省略します)。
タイプ別パターンマッチング
のようなパターンを型パターンms:List[_]
と呼びます。これは、 の型がに対して実行時にチェックされることを意味します。はワイルド カードであり、任意の型を意味するため、文字通り任意の型を意味します (型消去のため、 のようなパターンは使用できません。詳細については、このスレッドを参照してください)。ms
List[_]
_
ms:List[_]
List
ms:List[Int]
したがって、このコード スニペットのパターン全体の一致は、次のことを意味します。
ms
がリストの場合、結果は になります。そうでない場合flatten(ms)
、結果は になりますList(l)
。
このflatten
ように定義された関数は、リストをアンラップし、リストがなくなるまでこれを行う再帰関数であり、結果をList
!で再びラップします。この方法List[List[List.......[_]]]
は に変換されList[_]
ます。
型パターンの追加の例:
def hello(o: Any) = o match {
case _:Int => "Hi, I am an Int and I'm proud of it!"
case _:List[_] => "I have many things to tell you! I am a list after all!"
case b:Boolean => s"I'm simply a flag and my value is $b"
case _ => "I'm everything else you can imagine :D"
}
曖昧さ回避
ちなみに部分機能は、部分適用機能や部分適用とは全く違います!
Scala での部分関数の概念は、数学での部分関数と同じです。ドメインの値の一部が定義されていない可能性がある関数です。