Korlinでのimmutableについて
多次元配列をImmutableにしようとしてできていないのが現状である。
そこでまずKotlinにおけるImmutableとは何ぞや、と言うことについて検討してみた。
immutabilityを要求するのはFunctionalプログラミングにおいて顕著なのでその系統の書籍、例えばFunctional Kotlin1を中心に当たってみた。
Immutableとread onlyとは異なる
始めにこの事実を知っておこう。Kotlinにおいてvar宣言したものは変更でき、val宣言したものは変更できないとよく言われるが、実際の所val宣言したものがPrimitiveデータ型でなければ中身を変更することは可能である。
つまりval宣言してImmutableになるのはPrimitiveデータ型だけである。val宣言とは「read onlyとして宣言した」と言うのである。
val宣言では、どこがImmutableなのかというとそのオブジェクトへの「ReferenceがImmutableだ」と言う立場なのである。そしてread onlyではあっても様々なメソッドで中身を更新することが可能である。あとで実例を挙げる。
const valというもの
これはPrimitive型かStringに対してのみ使えるもので中身も定数である。オブジェクトのトップの属性としてしか使えず、クラスや関数の内部で宣言して使うことは出来ない。
C言語の記号定数のような使い道を想定しているのだろうか?自由度が低すぎてちょっと使えそうにない。
Immutable/Mutableなコレクション
KotlinにはArray以外にList, Set, Mapのコレクションがあり、基本はImmutableである。それぞれにMutableを冠したコレクションがあり、こちらはMutableである。
興味があるのは2次元配列をImmutableなListで実装すると言うことなので、それに沿って様々なパターンを実験してみた。
ここからはソースコードで見ていこう。
//Immutable 1D lists
val myIntList1 = listOf<Int>(1,2,3,4,5,6)
val myIntList2 = listOf<Int>(7,8,9,10,11,12)
//(Mutable) 1D arrays
val myIntArray1 = arrayOf(1,2,3,4,5,6)
val myIntArray2 = arrayOf(7,8,9,10,11,12)
//Mutable 1D lists
val myMutableIntList1 = mutableListOf<Int>(1,2,3,4,5,6)
val myMutableIntList2 = mutableListOf<Int>(7,8,9,10,11,12)
//Immutable 2D list
val my2DIntList : List<List<Int>> = listOf(myIntList1,myIntList2)
//(Mutable) 2D array
val my2dIntArray : Array<Array<Int>> = arrayOf(myIntArray1,myIntArray2)
//Mutable 2D list whose members are immutable
val my2DMutableList : MutableList<List<Int>> = mutableListOf(myIntList1,myIntList2)
//Mutable list whose members are also mutable
val my2DMutableMutableList : MutableList<MutableList<Int>> = mutableListOf(myMutableIntList1,myMutableIntList2)
//Immutable 2D list whose members are mutable
val my2DImmutableMutaableList : List<MutableList<Int>> = listOf(myMutableIntList1,myMutableIntList2)
二次元Listを作成する際にImmutableかMutableかを
- 全体に一律
- 内側の一次元Listと外側とで変える
パターンがあり、都合4通りできるのである。それらは結局
- Immutable
行列共にImmutableなList - Mutable
行列共にMutableなList - Outside immutable, inside mutable
新たな行を追加、存在する行を削除などは出来ないが、各一次元Listの中身は変更できる。 - Outside mutable, inside immutable
新たな行を追加、存在する行を削除などはできるが、一次元Listの中身は変更できない。
ここで注意すべきなのは、List宣言した所にMutableListを代入しても問題なくビルドされ、Mutableな振る舞いをすることである。逆にMutableList宣言した所にListを代入しようとするとエラーでビルドできない。
というものに帰着できる。FunctionalプログラミングのためにはImmutableなことが大事なので最初のものを使うことになりそう。
反転操作などの具体例
MutableなList, Arrayに行追加、反転操作を加えているサンプルプログラム。
fun main(args: Array<String>){
//Immutable 1D lists
val myIntList1 = listOf<Int>(1,2,3,4,5,6)
val myIntList2 = listOf<Int>(7,8,9,10,11,12)
//(Mutable) 1D arrays
val myIntArray1 = arrayOf(1,2,3,4,5,6)
val myIntArray2 = arrayOf(7,8,9,10,11,12)
//Mutable 1D lists
val myMutableIntList1 = mutableListOf<Int>(1,2,3,4,5,6)
val myMutableIntList2 = mutableListOf<Int>(7,8,9,10,11,12)
//Immutable 2D list
val my2DIntList : List<List<Int>> = listOf(myIntList1,myIntList2)
//(Mutable) 2D array
val my2dIntArray : Array<Array<Int>> = arrayOf(myIntArray1,myIntArray2)
//Mutable 2D list whose members are immutable
val my2DMutableList : MutableList<List<Int>> = mutableListOf(myIntList1,myIntList2)
//Mutable list whose members are also mutable
val my2DMutableMutableList : MutableList<MutableList<Int>> = mutableListOf(myMutableIntList1,myMutableIntList2)
//Immutable 2D list whose members are mutable
val my2DImmutableMutaableList : List<MutableList<Int>> = listOf(myMutableIntList1,myMutableIntList2)
println(my2DIntList)
println(my2dIntArray[0][1])
println(my2DMutableList)
println(my2DMutableMutableList)
//Reverse 1D array/list
myIntArray1.reverse()
myMutableIntList1.reverse()
println(my2DIntList)
println(my2dIntArray[0][1])
println(my2DMutableList)
//Put them back
myIntArray1.reverse()
myMutableIntList1.reverse()
//Add a row for the 2d list
my2DMutableList.add(listOf(13,14,15,16,17,18))
println(my2DMutableList)
//Reverse rows of 2d list
my2DMutableList.reverse()
println(my2DMutableList)
//Put them back
my2DMutableList.reverse()
//Reverse rows of 2d array
my2dIntArray.reverse()
println(my2dIntArray[0][1])
//Put them back
my2dIntArray.reverse()
//Reverse all elements
println(my2DMutableMutableList)
myMutableIntList1.reverse()
myMutableIntList2.reverse()
my2DMutableMutableList.reverse()
println(my2DMutableMutableList)
//Put them back
myMutableIntList1.reverse()
myMutableIntList2.reverse()
my2DMutableMutableList.reverse()
println(my2DMutableMutableList)
}
- Functional Kotlin, Mario Arias, Rivu Chakraborty, Packt Publishing 2018. ↩︎