2016/12/20の30分 Kotlinメモ👽

2016/12/20の30分 Kotlinメモ👽

書いてるコードは雑にここにあります。

github.com

Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。

Kotlinスタートブック -新しいAndroidプログラミング

Kotlinスタートブック -新しいAndroidプログラミング

Closures

Closuresなんぞ?という話は以下でなんとなくわかる。

qiita.com

getCounterメソッド内で宣言してるcount変数をreturnのLambdaの中で参照・変更できている。

要はLambdaの外側の関数で宣言されている変数の参照・変更ができる。

以下ではgetCounterを呼び出すごとに値がインクリメントされてるので、状態を保持することができる関数とも言えるのかな?

fun getCounter(): () -> Int {
    var count = 0

    return {
        count++
    }
}

println(counter()) // 0
println(counter()) // 1
println(counter()) // 2

Javaだと参照はできるけど、変更はできない。

var sum = 0
(1..10).forEach {
    sum += it
}
println(sum)

// Java
int sum = 0;
Stream.of(1, 2, 3)
        .forEach(integer -> {
            // Lambdaから外の変数の変更はできない
            sum += integer;
        });

public Supplier<Integer> getCounter() {
    int count = 0;
    return () -> {
        // Lambdaから外の変数の参照は可能
        return count;
    };
}

うむ。なのでJavaのLambdaはClosuresではない!と言われてるわけだ。

d.hatena.ne.jp

Closuresにあまり慣れていないからどういう場合に有効なのかは今後考えてみる

Inline Functions

kotlinlang.org

インライン展開することを意識して書く必要があるのね。

確かにKotlinの標準ライブラリはめっちゃinline付いてるなーと思ってたけど理由がなんとなくわかった。

// インライン関数
// 引数の関数オブジェクトがコンパイル時にインライン展開される
inline fun log(debug: Boolean = true, message: () -> String) {
    if (debug) {
        println(message())
    }
}

log { "debug log" }
log(false) { "debug log" }

crossinlineとかもいくつか使われてるの見たことあるなー

Non-local returns

Kotlin本では非ローカルリターンとラベルへのリターンと書いてある。

Lambdaの中でreturnすると関数から抜けるのね。

inline fun stringForEach(str: String, f: (Char) -> Unit) {
    for (c in str) f(c)
}

fun containsDigit(str: String): Boolean {
    stringForEach(str) {
        if (it.isDigit()) {
            println("containsDigit true")
            return true
        }
    }
    println("containsDigit false")
    return false
}

containsDigit("aaaaa") // containsDigit false
containsDigit("aaa1aa") // containsDigit true

Lambda自身から抜けたい時はラベルへのリターンを使えってことね。

ラベル名は自分でも付けられるけど、推論できる場合は関数名を指定できるらしい。

さっきのコードは結果がどうだったかを出力するのに2箇所にpirntln書いてたけど、ラベルへのリターンを使えば出力を1箇所に集約できる。

fun containsDigit2(str: String): Boolean {
    var result = false
    stringForEach(str) {
        if (it.isDigit()) {
            result = true
            return@stringForEach
        }
    }
    println("containsDigit $result")
    return result
}

containsDigit2("aaaaa") // containsDigit false
containsDigit2("aa1aaa") // containsDigit true

ラベルを自分でつけるならこんな感じ。

fun containsDigit3(str: String): Boolean {
    var result = false
    stringForEach(str) here@ {
        if (it.isDigit()) {
            result = true
            return@here
        }
    }
    println("containsDigit $result")
    return result
}

まとめ

30分以上やってますよ...はい...👽