可変長タプル on Kotlin?
https://qiita.com/yyu/items/4941417ec555ea5ab590 に触発されて一部をKotlinで実装してみようと思い立った。 ただし、元記事と違いがあり:
- List-likeに整数でindexアクセスする (拡張関数でgetterを定義することもできるものの、完全に保証しようと思えば$2^{32}-1$個の拡張関数を定義することになり、現実的ではない)
- 厳密に型は保証されない (右辺値自動推論を用いているが、期待通りの型であることを静的に保証しない―同上の理由によりすべてのTupleに対して静的に保証できない)
基本的な定義
sealed class Tuple
class Body<out A, out B : Tuple>(val head: A, val rest: B) : Tuple()
object Tail : Tuple()
サイズ
fun Tuple.size() = size(0)
tailrec fun Tuple.size(acc: Int): Int {
return when (this) {
is Body<*, *> -> this.rest.size(acc + 1)
Tail -> acc
}
}
取り出し
// auto
operator fun <E> Tuple.get(index: Int): E {
return get(0, index) as E
}
tailrec fun Tuple.get(acc: Int, index: Int): Any? {
if (acc == index) {
when (this) {
is Body<*, *> -> return head
Tail -> throw IndexOutOfBoundsException()
}
} else {
when (this) {
is Body<*, *> -> return this.rest.get(acc + 1, index)
Tail -> throw IndexOutOfBoundsException()
}
}
}
分解宣言
// usage:
// val (a, b, c, d) = tuple
// it's syntax-sugar for:
// val a = tuple[0]
// val b = tuple[1]
// val c = tuple[2]
// val d = tuple[3]
fun <E1> Body<E1, Tuple>.component1() = this[0] as E1
fun <E1, E2> Body<E1, Body<E2, Tuple>>.component2() = this[1] as E2
fun <E1, E2, E3> Body<E1, Body<E2, Body<E3, Tuple>>>.component2() = this[2] as E3
fun <E1, E2, E3, E4> Body<E1, Body<E2, Body<E3, Body<E4, Tuple>>>>.component2() = this[3] as E4
fun <E1, E2, E3, E4, E5> Body<E1, Body<E2, Body<E3, Body<E4, Body<E1, Tuple>>>>>.component2() = this[4] as E5
// あぁ!関数が加速度的に増えてゆく・・・