プログラマに優しい現実指向JVM言語 Kotlin入門

第3回 Kotlinを学ぶ

この記事を読むのに必要な時間:およそ 7 分

クロージャ

定義済みの関数を::により,関数オブジェクトに変換する一連の流れを介さずに関数オブジェクトを直接生成することもできます。リスト27の①と②と③では同じ結果を得られます。

リスト27 関数オブジェクトのリテラル表現

// ①
fun succ(n: Int): Int = n + 1
map(list, ::succ)

// ②
map(list, fun(n: Int): Int { return n + 1 })

// ③
map(list, {n: Int -> n + 1})

②や③のように関数オブジェクトを直接生成するような記法を関数リテラルと呼びます。ほかの言語ではラムダ式や無名関数と呼ばれるものです。③のような関数リテラルの書式を一般化すると次のようになります。

{引数リスト -> 関数本体}

関数リテラルには波括弧が必須であることに注意してください。また,文脈によっては省略した記述が可能になりますリスト28)⁠

リスト28 関数リテラル記法

// 関数リテラル内で型推論が働く
val foo: (Int) -> Int = {
  n -> n + 1
}

// 引数が1つの場合は暗黙の変数itが使える
val bar: (Int) -> Int = {
  it + 1
}

// 複数の文を持つ関数リテラル
val baz: (Int) -> Int = {
  var sum = 0
  for (e in 1..it) {
    sum += e
  }
  sum
}

// 高階関数に渡す特殊な記法
map(listOf(1, 2, 3)) {
  it + 1
}

ところで,Kotlinの関数リテラルはクロージャ(closure)です。つまり,引数に与えられた変数以外の変数を,コードを記述したときのスコープで解決できます。日本語で説明してもわかりづらいと思うので,コード例を示します。

今,リスト29に定義したような関数counterがあります。

リスト29 クロージャを返す関数counter

fun counter(): ()->Int {
  var count = 0
  return {
    count++
  }
}

この関数はIntを返す関数」を返します。Intを返す関数」を関数Aと呼ぶことにします。関数Aは{ count++ }のことです。変数countは関数Aの外で宣言されていますが,関数Aが定義されている場所でこれを参照,更新することができます。関数Aを呼び出すとcountの値を返して,countをインクリメントします。

関数counterの使用例がリスト30です。

リスト30 呼び出すたびにカウントアップする

val counter1 = counter()
println(counter1()) // => 0
println(counter1()) // => 1
println(counter1()) // => 2

val counter2 = counter()
println(counter2()) // => 0
println(counter2()) // => 1

counter()により関数Aを取得しています。関数A(ここではcounter1などと名前を付けています)が呼び出されるたびに,返される値が増えているのがわかります。

まとめ

Hello Worldプログラムを通じて,valvarによる変数宣言とStringテンプレート,コマンドライン引数を学びました。Kotlinのif-elseはJavaのそれとは異なり「式」であり,値を返します。forループは,Javaの拡張for文に似ており,ループカウンタを必要としません。

後半はKotlinの関数について学びました。関数の引数にはデフォルト値を設定しておくことができます。また関数呼び出し時に,引数を名前付きで渡せます。関数は第一級オブジェクトであり,関数型の変数に代入できます。その性質を利用した高階関数と,クロージャを学びました。Kotlinの関数はアノテーションを付けることで末尾呼び出し最適化やインライン関数化が有効になります。

次回はクラスについて解説します。

「インライン関数」

高階関数は強力なしくみですが,一般に呼び出しのコストが高い傾向にあります。関数オブジェクトの生成や呼び出しを伴うことがほとんどだからです。この問題を解消するためインライン関数というしくみが導入されています。インライン関数は,引数の関数リテラルがコンパイル時にインライン展開される関数のことです。通常の関数にinlineアノテーションを付加するだけでインライン関数になります。

リスト25をインライン関数にするには次のとおりです。

// inlineアノテーションを付けるだけ
inline fun map(ints: List<Int>, f: (Int) ->Int): List<Int> {
  val newList = java.util.ArrayList<Int>()
  for (e in ints) {
    newList.add(f(e))
  }
  return newList
}
Software Design

本誌最新号をチェック!
Software Design 2017年12月号

2017年11月18日発売
B5判/176ページ
定価(本体1,220円+税)

  • 第1特集
    ITエンジニアと数学
    数学プログラミング入門
  • 第2特集
    AIやディープラーニングで注目される
    気になるGPU超入門
  • 特別付録
    IIJ謹製「インターネット便利帳」

著者プロフィール

長澤太郎(ながさわたろう)

早稲田大学情報理工学科を2012年に卒業。同年入社したメーカー系SIerを経て,2013年にエムスリー株式会社へ入社。以来,世界の医療を変革するためソフトウェアエンジニアとして従事。

日本Kotlinユーザグループ代表,日本Javaユーザグループ幹事を務める。国内初となるKotlin入門書「Kotlinスタートブック」(リックテレコム)の著者。

ビールとディズニーが大好き。

コメント

コメントの記入