KotlinDive

プログラミング言語 Kotlin についての入門ブログです。

ラムダ式と匿名関数

匿名関数

ラムダ式では返り値の型を明示的に指定することができません。 明示的に指定できなくても、自動的に推測してくれるため、大抵の場合は問題ありません。
しかし、何らかの事情で返り値の型を明示的に指定したい場合、「匿名関数」を利用することができます。 匿名関数とは、その名前の通り関数名(関数の識別子)を持たない関数のことです。


fun(引数リスト): 返り値の型 = 式

fun(引数リスト): 返り値の型 {
    式
}

上記のように、関数名がないこと以外は普通の関数と同じです。
それでは、サンプルを見てみましょう。


fun main(args: Array) {
    val fruits = listOf("apple", "banana", "orange", "peach")

    // 関数名の前に::をつけることで関数を参照を取得できる
    val subList = filter(
            fruits,
            fun(str: String): Boolean = str.contains('p', true) )

    println(subList)
}

fun filter(source: List, condition: (String) -> Boolean): List {
    val ret = mutableListOf()

    for (i in source) {
        if (condition(i)) {
            ret.add(i)
        }
    }

    return ret;
}

[apple, peach]

関数リテラルと関数型

Kotlinにおいて、ラムダ式と匿名関数は関数リテラルとして扱われます。
数値リテラルや文字列リテラルなどと同じように、変数に代入することができます。


fun main(args: Array) {
    val fruits = listOf("apple", "banana", "orange", "peach")

    // 変数に代入できる
    val anonymous = fun(str: String): Boolean = str.contains('p', true)
    val lamda = { str: String -> str.contains('p', true) }

    val subList = filter(fruits, anonymous)
    println(subList)

    val subListLamda = filter(fruits, lamda)
    println(subListLamda)
}

fun filter(source: List, condition: (String) -> Boolean): List {
    val ret = mutableListOf()

    for (i in source) {
        if (condition(i)) {
            ret.add(i)
        }
    }

    return ret;
}

[apple, peach]
[apple, peach]

また、上記のサンプルのfilter関数のcondition引数のように、 引数の型と返り値の型から関数型を指定することができます。


fun main(args: Array) {
    val fruits = listOf("apple", "banana", "orange", "peach")

    // 型を指定して変数を宣言
    val anonymous: (String) -> Boolean
    val lamda: (String) -> Boolean

    anonymous = fun(str: String): Boolean = str.contains('p', true)
    // 引数の型が一つしかないことがわかっているので、itを利用できる
    lamda = { it.contains('p', true) }

    val subList = filter(fruits, anonymous)
    println(subList)

    val subListLamda = filter(fruits, lamda)
    println(subListLamda)
}

fun filter(source: List, condition: (String) -> Boolean): List {
    val ret = mutableListOf()

    for (i in source) {
        if (condition(i)) {
            ret.add(i)
        }
    }

    return ret;
}

[apple, peach]
[apple, peach]

クロージャ

ラムダ式や匿名関数の変数は、クロージャとして機能しています。
つまり、ラムダ式や匿名関数自身が定義されたスコープにアクセスできます。


fun main(args: Array) {
    val fruits = listOf("apple", "banana", "orange", "peach")

    var str = ""
    fruits.filter {
        it.contains('p', true)
    }.forEach {
        // このラムダ式が定義されてるスコープ、つまり「fun main」のスコープにアクセスできる。
        // そのため、fun mainスコープの変数であるstrを操作できる。
        str += it;
        str += "\n"
    }

    print(str);
}

apple
peach