2014年08月17日
Posted by 屋台ブルー at
2014年08月17日00:00 Comment(0)
Swiftで遊ぼう! - 38 サブスクリプト? 計算プロパティと何が違うの?

計算値プロパティとどのように使い分けるのでしょう。まあ、もう少しちゃんとした理解が必要なので、サブスクリプトの説明をみていく。
struct Matrix {
let rows:Int, columns:Int
var grid: [Double]
init(rows:Int, columns:Int){
self.rows = rows
self.columns = columns
grid = Array(count:rows * columns, repeatedValue:0.0)
}
func indexIsValidForRow(row:Int, column:Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row:Int, column:Int) -> Double {
get {
assert(indexIsValidForRow(row, column:column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValidForRow(row, column:column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
var matrix = Matrix(rows:2, columns:2)
上記のコードをじーっと眺めて読んでいく。私は一月前までの私ではない! 少しは読めるようになってきているので、ちょっと解説をつけて説明をしよう。
1) 最初に構造型Matrixの箱作り宣言。
2) このMatrixには3つのプロパティが定義されている。Intタイプのrowsとcolumns、そしてDoubleタイプの配列型gridだ。さて、注意しなければならないのが、ここでデフォルト型の初期化は行われていない。いままでよくありがちな、「let rows:Int=0」のような宣言です。
3) デフォルトプロパティ初期化がされていないので、初期化の記述が必要になります。そこで、ここで宣言されているinit()はメンバーワイズ初期化のステップがとられている。ということは、インスタンスを宣言するときに必ず初期化パラメーターに引数を指定しないとエラーになる。また、初期化の時の引数代入にはパラメーター名を表記しなければならない。これは関数の時と挙動が違うので間違えないように。
4) おっと、次の関数indexIsValidForRowも構造体のプロパティになるのかな。後で指定されるrowとcolumnの値が範囲を超えているかどうかの判定をする関数だ。範囲内にあればtrueを返し、範囲をオーバーしていたらfalseを返すだけ。
5) そして、次に出てくるサブスクリプトがこの構造体のミソになる。実は、この構造体はrowsで指定する最大行数とcolumnsで指定された最大列数の数字と、それで構成される格子数(rows×columns)の配列型gridしか与えられていない。ということは、gridの順番は一直線上と考えていいだろう。それを擬似的に格子状に扱って、アクセスする方法をサブスクリプトの形で実現していることになる。
というのもgridというのは配列なんで、以下のようにエレメント値にアクセスすることができる。
matrix.grid[1] = 3.0
すると、gridの配列は[0.0,3.0,0.0,0.0]
こうなるが、こんなやり方をすれば思惑が外れて意味がないよね。
6) そこで格子状にエレメントにアクセスするサブスクリプトがgetで定義されている。その前のassertはデバック作業なんで範囲外の数字を入力するとプログラムが中断してエラーメッセージを返す。範囲内なら、格子状の指定[x,y]を直線上の配列インデックスに一致させる計算式「row×columns]+column」で指定された配列のエレメント値をDouble型で返す。
7) 新しく配列エレメント値を変更したいときは、setで定義されているように指定された配列に新しいnewValue「←指定がなければデフォルトです。これは計算型プロパティと同じだよね。」が組み込まれる。
と、こんな風に説明できるのでしょう。おお、なんか少し理解できているじゃないですか私。今日はこの辺にしとこう。では。