見出し画像

Swiftでプログラミング。-Subscripts

クラス、構造体、および列挙型では、コレクション、リスト、またはシーケンスのメンバ要素にアクセスするためのショートカットである添え字を定義できます。添え字を使用すると、設定および取得のための個別のメソッドを必要とせずに、インデックスによって値を設定および取得できます。例えば、配列の要素には someArray[index] としてアクセスし、Dictionary インスタンスの要素には someDictionary[key] としてアクセスします。

1 つの型に対して複数の添え字を定義することができ、適切な添え字は、添え字に渡したインデックスの値に基づいて選択されます。添え字は 1 つに限定されず、必要な複数の入力パラメータの添え字を定義することができます。

Subscript Syntax

添え字を使うと、インスタンス名の後に角括弧で1つまたは複数の値を記述して、値を得ることができます。添え字の構文は、インスタンス・メソッドの構文と計算プロパティの構文に似ています。添え字定義は subscript キーワードで記述し、インスタンス・メソッドと同じ方法で 1 つ以上の入力パラメータと戻り値の型を指定します。インスタンス・メソッドとは異なり、添え字は読み取り/書き込みまたは読み取り専用にできます。この動作は、計算プロパティと同じ方法でゲッターとセッターによって伝達されます。

   subscript(index: Int) -> Int {
       get {
           // Return an appropriate subscript value here.
       }
       set(newValue) {
           // Perform a suitable setting action here.
       }
   }

newValue のタイプは、添え字の戻り値と同じです。計算プロパティと同様に、セッターの (newValue) パラメータを指定しないことを選択できます。newValue という既定のパラメータは、自分で指定しない場合、セッターに提供されます。

読み取り専用の計算プロパティと同様に、get キーワードとその中括弧を削除することで、読み取り専用の添え字の宣言を簡略化することができます。

    subscript(index: Int) -> Int {
       // Return an appropriate subscript value here.
   }

ここでは、読み取り専用の添え字の実装例として、整数のn倍の表を表すTimesTable構造体を定義しています。

    struct TimesTable {
       let multiplier: Int
       subscript(index: Int) -> Int {
           return multiplier * index
       }
   }
   let threeTimesTable = TimesTable(multiplier: 3)
   print("six times three is \(threeTimesTable[6])")
   // Prints "six times three is 18"

この例では、TimesTableの新しいインスタンスを作成して、3倍の表を表現しています。これは、インスタンスの乗数パラメータに使用する値として、構造体のイニシャライザに値 3 を渡すことで示されます。

threeTimesTable[6]の呼び出しに示されているように、その添え字を呼び出すことによって、threeTimesTableインスタンスを照会することができます。これにより、3 回表の 6 番目のエントリが要求され、6 の 3 倍である 18 の値が返されます。

n倍表は、固定の数学的ルールに基づいています。threeTimesTable[someIndex] を新しい値に設定することは適切ではないため、TimesTable の添え字は読み取り専用の添え字として定義されています。

Subscript Usage

添え字は、何に使用されるかよって異なります。添え字は通常、コレクション、リスト、または連続した値のメンバ要素にアクセスするためのショートカットとして使用されます。特定のクラスや構造体の機能に最も適した方法で、自由に添え字を実装することができます。

たとえば、Swift の Dictionary 型は、Dictionary インスタンスに格納されている値を設定および取得するための添え字を実装します。添え字の括弧内にDictionaryのキー型のキーを提供し、Dictionaryの値型の値を添え字に割り当てることで、Dictionaryに値を設定できます。

   var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
   numberOfLegs["bird"] = 2

上記の例では、numberOfLegs という変数を定義し、3 つのキーと値のペアを含むディクショナリテラルで初期化しています。numberOfLegs ディクショナリの型は、[String: Int] と推測されます。この例では、ディクショナリを作成した後、添え字の割り当てを使用して、String の "bird" と Int の "2" をディクショナリに追加します。

Swift の Dictionary 型は、optional 型を取り、返す添え字として、そのキーと値の添え字を実装します。上記の numberOfLegs 辞書の場合、キー値の添え字は "Int?"、つまり「optional int」型の値を取り、返します。Dictionary 型では、オプションの添え字タイプを使用して、すべてのキーが値を持つわけではないという事実をモデル化し、そのキーに nil 値を割り当てることでキーの値を削除する方法を提供しています。

Subscript Options

添え字は任意の数の入力パラメータを取ることができ、これらの入力パラメータは任意の型をとることができます。添え字は、任意の型の値を返すこともできます。

関数と同様に、添え字はさまざまな数のパラメータを取ることができ、「可変パラメータとパラメータのデフォルト値」で説明したように、パラメータのデフォルト値を提供することができます。ただし、関数とは異なり、添え字はインアウト・パラメータを使用できません。

クラスまたは構造体は、必要な数の添え字実装を提供することができ、使用される適切な添え字は、添え字が使用される時点で添え字の括弧内に含まれる値の型に基づいて推測されます。この複数の添え字の定義は、subscript overloadingとなります。

添え字は1 つのパラメータを取るのが最も一般的ですが、複数のパラメータを持つ添え字を定義することもできます。次の例では、Double 値の 2 次元行列を表す Matrix 構造体を定義しています。Matrix 構造体の添え字は、2 つの整数パラメータを取ります。

    struct Matrix {
       let rows: Int, columns: Int
       var grid: [Double]
       init(rows: Int, columns: Int) {
           self.rows = rows
           self.columns = columns
           grid = Array(repeating: 0.0, count: rows * columns)
       }
       func indexIsValid(row: Int, column: Int) -> Bool {
           return row >= 0 && row < rows && column >= 0 && column < columns
       }
       subscript(row: Int, column: Int) -> Double {
           get {
               assert(indexIsValid(row: row, column: column), "Index out of range")
               return grid[(row * columns) + column]
           }
           set {
               assert(indexIsValid(row: row, column: column), "Index out of range")
               grid[(row * columns) + column] = newValue
           }
       }
   }

Matrixは,rowとcolumnsという2つのパラメータを受け取り,Double型のrow * columnsの値を格納するのに十分な大きさの配列を作成するイニシャライザを提供しています.行列の各位置には、0.0 の初期値が与えられます。これを実現するために,配列のサイズとセルの初期値 0.0 が配列のイニシャライザに渡され,正しいサイズの新しい配列を作成して初期化します.このイニシャライザについては、「デフォルト値を持つ配列の作成」で詳しく説明しています。

適切な行数と列数をイニシャライザに渡すことで、新しい行列のインスタンスを作成することができます。

var matrix = Matrix(rows: 2, columns: 2)

上の例では,2行2列の新しい Matrix インスタンスを作成しています.この Matrix インスタンスのグリッド配列は、左上から右下に向かって読むように、効果的に行列のフラット化されたバージョンです。

行列の値は、行と列の値をカンマで区切って添え字に渡すことで設定できます。

   matrix[0, 1] = 1.5
   matrix[1, 0] = 3.2

これらの2つのコードは、添え字のセッターを呼び出して、行列の右上の位置(行が0、列が1の位置)に1.5、左下の位置(行が1、列が0の位置)に3.2の値を設定します。

Matrix 添え字のゲッターとセッターの両方に、添え字の行と列の値が有効であることをチェックするアサーションが含まれています。これらのアサーションを支援するために、Matrix には indexIsValid(row:column:) という便利なメソッドがあり、要求された行と列が行列の範囲内にあるかどうかをチェックします。

    func indexIsValid(row: Int, column: Int) -> Bool {
       return row >= 0 && row < rows && column >= 0 && column < columns
   }

行列の範囲外にある添え字にアクセスしようとすると、条件のチェックをします(assert)。

    let someValue = matrix[2, 2]
   // This triggers an assert, because [2, 2] is outside of the matrix bounds.

Type Subscripts

前述のように、添え字(Subscript)は、特定の型のインスタンスに呼び出す添え字です。また、型自体に呼び出される添え字を定義することもできます。この種の添え字は、type subscriptと呼ばれます。添え字キーワードの前に static キーワードを記述することで、型の添え字を示します。クラスは、代わりに class キーワードを使用して、サブクラスがその添え字のスーパークラスの実装をオーバーライドできるようにすることができます。以下の例は、型の添え字を定義して呼び出す方法を示しています。

    enum Planet: Int {
       case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
       static subscript(n: Int) -> Planet {
           return Planet(rawValue: n)!
       }
   }
   let mars = Planet[4]
   print(mars)

この記事が気に入ったらサポートをしてみませんか?