高階関数とは
関数を引数や戻り値に取るような関数は、関数に関数を積み重ねている 事から、建物の2階建て、3階建て構造に見立てて高階関数(Higher Order Functions)といいます。
- 英語表記を直訳するとおおよそ ハイレベルな関数呼び出し となりますが、日本語訳の建物の比喩の方が個人的にはわかりやすいと感じます
Kotlinの高階関数
Kotlinでも高階関数をサポートしています。関数は第1級オブジェクト(1st object)としてあつかうことができます。
第1級オブジェクトができること
ここで、第1級オブジェクトは以下のようなことができます。
- 無名関数、すなわち関数リテラルとして表現できます
- 変数に代入することができます
- 関数の引数に渡すことができます
- 関数の戻り値として返すことができます
ここでは高階関数のコード例をもとにして高階関数の定義と利用方法についてかんたんに説明します。
関数リテラルとは
関数リテラルとは、関数を fun キーワードで定義しないで、直接、波括弧 「{ }」 内に記述する記法です。関数リテラル記法で記述すると、コードが即座にそのまま式として渡せます。この式のことを ラムダ式 といいます。
なお、関数リテラルは、関数定義による関数名を持たない無名関数を記述するときにも用います。
高階関数の例
以下の例は、関数 readList() の引数に、別の関数 someFunction() を与えて定義する例です。
fun <A>readList(
members: List<A>, someFunction: (A) -> Unit ) {
for( person in members ) {
someFunction( person )
}
}
fun main( args: Array<String> ) {
val members = listOf(
"Tom", "Jerry" ,"Bob", "Olibia" )
val numbers = listOf( 1, 3, 5, 7 )
readList( members, { member -> println( member ) } )
readList( numbers, ::println )
}
実行結果
Tom
Jerry
Bob
Olibia
1
3
5
7
機能
この関数 readList() は、第1引数に与えられた List 型のコレクションのメンバーを、第2引数に与えられた関数 someFunction() で処理する関数です。
処理内容
関数 readList() の処理内容は、第1引数に与えられた List 型の要素を順番に取り出して、第2引数に与えられた関数 someFunction() に渡して処理しています。
引数に与えられた関数の定義
型パラメータ を指定することによって型を規定しています。ここでは型は A になるとしています。
具体的には、第2引数に与えられた関数 someFunction() は引数に型 A を取り、戻り値に何も返さない( Unit )という定義をしています。
なお、型 A は、関数 readList() の第1引数に与えられた List 型の要素の型と同じであると定義しています。
ジェネリクス(型パラメータ)とは
このコード例ように、関数の引数や戻り値の型を規定する(縛る)ことをJavaではジェネリクス(Generics:総称型)といいます。
Kotlinが言語仕様を決めるのに参考にしたと言われる関数型プログラミング言語Scalaでは型パラメータと呼ぶことがあります。Kotlinでも同じようにジェネリクスまたは型パラメータというようです。
高階関数の利用
リスト型のコレクションオブジェクトを ファクトリ関数 listOf() を用いて定義しています。
1つ目が文字列をメンバーに持つコレクションの定義です。
2つ目が整数をメンバーに持つコレクションの定義です。
これらのコレクションを関数 readList の第1引数に与えています。第2引数はこれらのコレクションの要素を引数に持つ println() 関数を渡しています。
1つ目の関数渡し:ラムダ式
{ member -> println( member ) }
これは、ラムダ式による引数への関数渡しを行っています。ラムダ式は、波括弧 「{ }」 内に記述します。
このラムダ式の意味は、
コレクション members の各要素が member として渡されるので、渡された要素を println() 関数で出力せよ!
ということです。
ラムダ式とは
ここで、ラムダ式は、上で説明した 関数リテラル 形式で記述された式のことです。この例のように、関数を波括弧 「{ }」 内に直接記述したものです。
2つ目の関数渡し:関数への参照渡し
::println
println() 関数への参照を渡しています。高階関数を引数として渡す場合には、このように関数への参照を :: 記号を関数の先頭に付加します。
ここで、println() や println のように関数名のみで :: 記号を付けないで引数に渡すとコンパイルエラーになりますので注意が必要です。