質問の背景:
サブスクリプトは、コレクション、構造体、その他のデータ型の内部の値に対して、角括弧を使用してアクセスするためのメカニズムです。これは、Swiftに追加されたもので、他の言語(たとえば、Pythonの配列のインデックス化、C++/Javaの[]演算子)と同様の方法です。これにより、コレクションや類似のオブジェクトを扱う際に、より直感的になります。
問題:
すべての型がデフォルトで配列のようにインデックスによるアクセスをサポートしているわけではありません。データを持つ構造体やクラスがある場合、要素へのアクセスは、メソッドを使うのではなく、慣れ親しんだ構文(例えば、myObject[index])を使用する方が論理的です。
解決策:
Swiftでは、任意のユーザー定義型に対してサブスクリプトを独自に実装することができます。読み取り専用としても、書き込み専用としても作成できます。以下は、正方行列を操作するためのサブスクリプトを実装した構造体の例です:
struct Matrix { let size: Int private var grid: [Int] init(size: Int) { self.size = size self.grid = Array(repeating: 0, count: size * size) } subscript(row: Int, column: Int) -> Int { get { precondition(indexIsValid(row, column), "範囲外のインデックス") return grid[(row * size) + column] } set { precondition(indexIsValid(row, column), "範囲外のインデックス") grid[(row * size) + column] = newValue } } private func indexIsValid(_ row: Int, _ column: Int) -> Bool { return row >= 0 && row < size && column >= 0 && column < size } } var m = Matrix(size: 3) m[1,2] = 7 print(m[1,2]) // 7
重要な特徴:
subscriptを使用して実装され、get専用またはget/setのいずれかにすることができます。サブスクリプトはコレクションにしか使用できないのですか?
いいえ、サブスクリプトは構造体、クラス、列挙型など、あらゆる型に対して実装できます。データ型が必ずしもコレクションである必要はなく、サブスクリプトを介してリクエストを処理するロジックが重要です。
同じ型に異なるシグネチャの複数のサブスクリプトを作成できますか?
はい。異なるパラメータのシグネチャでサブスクリプトをオーバーロードすることが許可されています:
struct Example { subscript(index: Int) -> Int { return index } subscript(key: String) -> String { return key.uppercased() } }
setなしで計算済みプロパティとしてサブスクリプトを使用することは許可されていますか?
はい、サブスクリプトは読み取り専用(get-only)であり、その場合、書き込みを試みるとコンパイルエラーが発生します。
struct ReadOnly { subscript(index: Int) -> Int { index * index } }
大きな構造体が範囲チェックなしでサブスクリプトを実装しており、不正なインデックスによりアプリケーションがクラッシュします。
利点:
欠点:
Matrixはインデックスの範囲に関してpreconditionを実装しています。エラーはすぐにキャッチされ、プロダクションには入りません。
利点:
欠点: