今でもたまにやらかすので供養として。
Kotlinにはいくつか便利な関数がありますが、その中にjoinToString
があります。
joinToString - Kotlin Programming Language
上に貼ったリンク先でも読めますが、Iterable
ただ、便利なあまりたまに紛らわしい挙動をすることがあります。
例えばこんなふうに。
fun main() { val list = listOf("a", "b", "c") // 1 println(list.joinToString(",")) // 2 println(list.joinToString { "," }) kotlin.system.exitProcess(0) }
joinToString
関数を使った処理を2つ書いてみました。遠目に見ると同じ処理に見えなくもないです。
いやいや、全然違うじゃん、という人も、出力結果を予想しながら続きを読んでみてください。
fun main() { val list = listOf("a", "b", "c") // 1 println(list.joinToString(",")) // a,b,c // 2 println(list.joinToString { "," }) // ,, ,, , kotlin.system.exitProcess(0) }
全く違う結果になりました。不思議ですね。
これは、Kotlinのコーディング規約と、joinToStringの引数の合わせ技によって起こっています。ここから先は気になった人だけ読んでみてください。
Kotlinのコーディング規約
Kotlinのコーディング規約の中には以下のようなものがあります。
According to Kotlin convention, if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses: Higher-order functions and lambdas | Kotlin Documentation
つまり、関数の最後の引数が関数であった場合は、その部分だけラムダ式としてカッコの外側に書き出すことができる、ということなのです。
実際に以下のようなコードを見たことがある人もいるのではないでしょうか。
val product = items.fold(1) { acc, e -> acc * e }
上記はfold関数の最後の引数は、operation: (acc: R, T) -> R
という、各要素に対する操作を表す関数です。よって、関数部分に当たるラムダ式がカッコの外側に書き出されている、というわけです。
今回のjoinToString関数も、transform: ((T) -> CharSequence)? = null
という関数になっており、{ "," }
というラムダ式で、常に","
が返ってくる関数になってしまっている状態というわけです。
ちなみに、以下のページにて、Kotlinの開発元である、Jetbrainsがコーディング規約をまとめています。気になった人は覗いてみてください。
Coding conventions | Kotlin Documentation
joinToStringの引数
上でも少し触れましたが、joinToStringの最後の引数は関数を受け取ります。
では、最初の引数は何か、というと、separator
、つまり区切り文字を表します。
ちなみにこのseparator
には、デフォルト値として", "
が与えられています。
つまり、以下のコードは「", "
を区切り文字として」「要素を常に","
に変化させる関数を適用する」ことによって生まれた出力ということになります。
println(list.joinToString { "," }) // ,, ,, ,
おわりに
joinToString
がなんだかまぎらわしい関数のように思えてきた人もいるかもしれませんが、他にもprefix/postfixを指定できたり、いろいろな機能があります。
val numbers = listOf(1, 2, 3) println(numbers.joinToString()) // 1, 2, 3 println(numbers.joinToString(prefix = "[", postfix = "]")) // [1, 2, 3] println(numbers.joinToString(prefix = "<", postfix = ">", separator = "+")) // <1+2+3>
気になった人は以下のPlaygroundで実際に触って確かめてみてください。