その1 で独自に makeSubList という再帰関数を定義して解決していたが、 fold を使いさえすれば済む話ではないかと思っていたので、今回試した。
結論としては fold で書き直すことができた。
// main.kts
tailrec fun makeSubList(acc: MutableList<List<String>>, list:List<String>, n:Int): List<List<String>> {
return if( list.isEmpty() ){
acc
} else {
val subList = list.take(n)
acc.add(subList)
makeSubList(acc, list.drop(n), n)
}
}
val list = listOf("1","2","3","4","5","6","7","8")
val subList = makeSubList(mutableListOf(), list, 3)
println( subList ) // [[1, 2, 3], [4, 5, 6], [7, 8]]
fold のシグニチャ:
inline fun <T, R> Array<out T>.fold(
initial: R,
operation: (acc: R, T) -> R
): R
このコンテキストにおける T,R を わかりやすくするため次のように typealias で定義しておく。
typealias TypeT = String
typealias TypeR = List<List<String>>
それではサブリストを fold を使ってつくるために 必要な initial , operation を定義:
val n = 3
val initial: TypeR = listOf(listOf())
val operation: (TypeR, TypeT)->TypeR = { acc, itemValue->
if( acc.isEmpty() ){
val newSubList = listOf(itemValue)
acc + listOf(newSubList)
} else {
val lastSubList = acc.last()
if( lastSubList.size < n ){
val newSubList = lastSubList + listOf(itemValue)
acc.take( acc.size-1 ) + listOf(newSubList)
} else {
val newSubList = listOf(itemValue)
acc + listOf(newSubList)
}
}
}
この initial , operation と fold を使ってサブリストをつくる:
val list = listOf("1","2","3","4","5","6","7","8")
val subListList = list.fold(initial, operation)
println(subListList) // [1, 2, 3], [4, 5, 6], [7, 8]]
n は fold 関数に含めることができないので、 ここではグローバル変数として与えている。 これは少し気持悪い。 そこで、全体を MakeSubList というクラスで囲むことにする。
// main.kts
typealias TypeT = String
typealias TypeR = List<List<String>>
class MakeSubList(private val n: Int){
val initial: TypeR = listOf(listOf())
val operation: (TypeR, TypeT)->TypeR = { acc, itemValue->
if( acc.isEmpty() ){
val newSubList = listOf(itemValue)
acc + listOf(newSubList)
} else {
val lastSubList = acc.last()
if( lastSubList.size < n ){
val newSubList = lastSubList + listOf(itemValue)
acc.take( acc.size-1 ) + listOf(newSubList)
} else {
val newSubList = listOf(itemValue)
acc + listOf(newSubList)
}
}
}
val run: (List<String>)->TypeR = { it.fold(initial, operation) }
}
val list = listOf("1","2","3","4","5","6","7","8")
println(MakeSubList(2).run(list)) // [[1, 2], [3, 4], [5, 6], [7, 8]]
println(MakeSubList(3).run(list)) // [[1, 2, 3], [4, 5, 6], [7, 8]]
println(MakeSubList(4).run(list)) // [[1, 2, 3, 4], [5, 6, 7, 8]]
println(MakeSubList(5).run(list)) // [[1, 2, 3, 4, 5], [6, 7, 8]]
run 関数を定義してそこにリスト(List<String>)を渡すと結果のサブリストが返るようにした。 n は MakeSubList のコンストラクタに渡す。
n だけのためにわざわざ MakeSubList クラスをつくるのはちょっと微妙。 いやまて、 クラスを使うのではなく、単純に toSubList 関数を定義すればよいだけではないか? このように:
// main.kts
typealias TypeT = String
typealias TypeR = List<List<String>>
val toSubListList: (List<String>, Int)->TypeR = { list, n->
val initial: TypeR = listOf(listOf())
val operation: (TypeR, TypeT)->TypeR = { acc, itemValue->
if( acc.isEmpty() ){
val newSubList = listOf(itemValue)
acc + listOf(newSubList)
} else {
val lastSubList = acc.last()
if( lastSubList.size < n ){
val newSubList = lastSubList + listOf(itemValue)
acc.take( acc.size-1 ) + listOf(newSubList)
} else {
val newSubList = listOf(itemValue)
acc + listOf(newSubList)
}
}
}
list.fold(initial, operation)
}
val list = listOf("1","2","3","4","5","6","7","8")
println(toSubListList(list, 2)) // [[1, 2], [3, 4], [5, 6], [7, 8]]
println(toSubListList(list, 3)) // [[1, 2, 3], [4, 5, 6], [7, 8]]
println(toSubListList(list, 4)) // [[1, 2, 3, 4], [5, 6, 7, 8]]
println(toSubListList(list, 5)) // [[1, 2, 3, 4, 5], [6, 7, 8]]
これで、サブリスト(リスト)を作り出すために独自の再帰関数ではなく fold を使って実現できました。
その2 の改良。
そもそもサブリスト(リスト)をつくるのに、自前再帰関数や fold 使うなど仰々しいことをしないで subList ( https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/sub-list.html ) を使えば解決できる。
listOf("1","2","3","4","5","6","7","8").subList(0,3)
これは次のリストを生み出す:
[1, 2, 3]
だからこのリストをたとえば 3個ずつのリスト に分割したければ:
val list = listOf("1","2","3","4","5","6","7","8")
list.subList(0,3) // [1, 2, 3]
list.subList(3,6) // [4, 5, 6]
list.subList(6,8) // [7, 8]
最後のサブリスト作成時の from, to index 指定にさえ注意すれば問題ない。
あとは サブリストの開始index のリスト [0, 3, 6] を生み出す方法を考えればよい。 それは次のようにする:
0.until(list.size).step(3).map { it } // [0, 3, 6]
それでは、 この方法で先ほどと同じように使える MakeSubList クラスをつくってみる。
// main.kts
import kotlin.math.min
typealias TypeT = String
typealias TypeR = List<List<String>>
class MakeSubList(private val n: Int){
val run: (List<TypeT>)->TypeR = { list->
0.until(list.size).step(n).map { i->
val fromIndex = i
val toIndex = min(fromIndex + n, list.size)
list.subList(fromIndex, toIndex)
}
}
}
val list = listOf("1","2","3","4","5","6","7","8")
println(MakeSubList(2).run(list)) // [[1, 2], [3, 4], [5, 6], [7, 8]]
println(MakeSubList(3).run(list)) // [[1, 2, 3], [4, 5, 6], [7, 8]]
println(MakeSubList(4).run(list)) // [[1, 2, 3, 4], [5, 6, 7, 8]]
println(MakeSubList(5).run(list)) // [[1, 2, 3, 4, 5], [6, 7, 8]]
これだけでサブリスト(リスト)を生成できました。
これならわざわざ MakeSubList クラスをつくるまでもない。 代わりに次の toSubListList 関数だけで事足りる。
import kotlin.math.min
typealias TypeT = String
typealias TypeR = List<List<String>>
val toSubListList: (List<String>, Int)->TypeR = { list, n->
0.until(list.size).step(n).map { i->
val fromIndex = i
val toIndex = min(fromIndex + n, list.size)
list.subList(fromIndex, toIndex)
}
}
val list = listOf("1","2","3","4","5","6","7","8")
println(toSubListList(list, 2)) // [[1, 2], [3, 4], [5, 6], [7, 8]]
println(toSubListList(list, 3)) // [[1, 2, 3], [4, 5, 6], [7, 8]]
println(toSubListList(list, 4)) // [[1, 2, 3, 4], [5, 6, 7, 8]]
println(toSubListList(list, 5)) // [[1, 2, 3, 4, 5], [6, 7, 8]]
以上です。