今回は第一級オブジェクトの定義として示した4つの機能、その最後である「関数を戻り値として返す関数」です。
ただこのトピックは少々深くなるので、こちらの記事ではそのような高階関数の定義と呼び出し方に絞ってお伝えします。
関数を返す関数
関数を引数に取る関数は前回お話しました。では関数を戻り値として返すとはどういうことなのか。
まずはコードを見てみましょう。
fun greet(): (String)->String{
return {
val ans = when{
it.contains("幸楽")->"角野卓造じゃねえよ!"
it.contains("緑")->"シュレックじゃねえよ!"
it.contains("クッキー")->"ステラおばさんじゃねえよ!"
else->"イジれよ!"
}
ans
}
}
fun 関数名(関数のパラメータ): (ラムダのパラメータ)->ラムダの結果の型{
処理内容
return {ラムダ式}
}
関数greetは引数を取らず、戻り値は「String型を取り、String型を返す関数」です。
(String)->Stringの部分は「関数名(パラメータ): 」の後に置かれているため、この関数の戻り値であることが分かります。
この関数の場合は直後にreturnが来ていますが、このreturnの前に通常の関数と同じように、何らかの処理を行ってもかまいません。大事なのはreturnの後です。
関数のreturnの後はラムダ式
returnの後には波括弧{}で囲まれたラムダ式が来ています。
引数Stringはこの場合1つなので、ラムダ内ではitとして扱われるために->は省略されていますが、2つ以上であれば何らかの名前を付けて処理の前に->を記述してください。ここはラムダ式の基本と同じです。
ラムダ式の構文についてはこちらの記事を参考にしていただければと思います。
処理としてはwhen式で分岐させてString型を返すだけです。引数itの中に指定した文字が含まれているかどうかをcontainsメソッドで判断し、その結果によってツッコミが変化します。
さて、この関数を呼び出すにはどうすればいいでしょう?
呼び出し方
上記の高階関数、このような呼び出しは不可です。
println(greet("幸楽"))
エラー:(2, 19) Kotlin: Too many arguments for public fun greet(): (String) -> String defined in root package in file Start.kt
greet関数に渡す引数が多すぎる
これは当然。greet関数はあくまで引数無しの関数です。では試しにこうしてみましょう。
println(greet())
//(kotlin.String) -> kotlin.String
これはエラーにはなりませんが、標準出力の結果は「Stringを引数にとり、Stringを返す関数」という表記だけになってしまいます。ラムダ式に引数を渡すこともできません。
println(greet()("幸楽"))
//角野卓造じゃねえよ!
一応これでも動作はしますが、若干の気持ち悪さが残ります。それではこうしてみましょう。
val haruna = greet()
println(haruna("クッキー"))
//ステラおばさんじゃねえよ!
関数を返す関数を変数に代入する
ラムダ式の基本の記事ではこのような代入は例外であると書いています。
「関数を返す関数」でなければ、「::greet」のように関数オブジェクトにしなければ、関数自体を変数に代入していることになりません。通常の関数で上のような代入をした場合は、関数の戻り値が代入されるだけになってしまいます。
ただ戻り値が関数であればこの限りではありません。この代入は関数オブジェクトを用いた代入と同じように、それ自体が関数として働きます。
上のコードで変数harunaは、greet関数の戻り値であるラムダ式が入った(ラムダ式を参照した)変数です。
まだまだ深い関数
今回は関数を返す関数の定義と、その呼び出し方に絞ってお伝えしました。
ここから話はスコープ、クロージャへとつながっていきます。
このクロージャに関しては他の前提知識が必要なため、量が膨大になってしまうため別記事にまとめます。
次回はプログラムのスコープについて。