Swift 言語ガイド – 12. サブスクリプト

swift

はじめに

公式ページのSwift 言語ガイド「The Swift Programming Language Swift 5.3」に基づき記載いたします。本ページでは、「サブスクリプト」について記載いたします。
記載内容に誤り等ございましたら、ご連絡をいただければ幸いです。

12. サブスクリプト

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

1つの型に複数のサブスクリプトを定義でき、サブスクリプトに渡すインデックス値の型に基づいて、使用する適切なサブスクリプトオーバーロードが選択されます。サブスクリプトは単一の次元に限定されず、カスタム型のニーズに合わせて、複数の入力パラメータを使用してサブスクリプトを定義できます。

12.1. サブスクリプト構文

サブスクリプトを使用すると、インスタンス名の後に角かっこで囲まれた1つ以上の値を書き込むことにより、型のインスタンスをクエリできます。それらの構文は、インスタンスメソッド構文と計算プロパティ構文の両方に似ています。インスタンスメソッドと同じ方法で、subscriptキーワードを使用してサブスクリプト定義を記述し、1つ以上の入力パラメータと戻り値の型を指定します。インスタンスメソッドとは異なり、サブスクリプトは読み取り/書き込みまたは読み取り専用にすることができます。この動作は、計算型プロパティの場合と同じ方法で、ゲッターとセッターによって伝達されます。

subscript(index: Int) -> Int {
    get {
        // ここで適切なサブスクリプト値を返します
    }
    set(newValue) {
        // ここで適切な設定アクションを実行します
    }
}

newValueの型は、サブスクリプトの戻り値と同じです。計算型プロパティと同様に、セッターの(newValue)パラメータを指定しないことを選択できます。自分で指定しない場合は、newValueというデフォルトのパラメータがセッターに提供されます。

読み取り専用の計算型プロパティと同様に、getキーワードとその中括弧を削除することで、読み取り専用のサブスクリプトの宣言を簡略化できます。

subscript(index: Int) -> Int {
    // ここで適切なサブスクリプト値を返します
}

読み取り専用のサブスクリプト実装の例を次に示します。これは、整数の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])")
// "six times three is 18"を出力します

この例では、3倍の九九を表すためにTimesTableの新しいインスタンスが作成されます。これは、インスタンスの乗数パラメータに使用する値として、構造体の初期化子に値3を渡すことで示されます。

threeTimesTable[6]の呼び出しに示されているように、サブスクリプトを呼び出すことにより、threeTimesTableインスタンスをクエリできます。これにより、3倍の九九の6番目のエントリが要求され、18、つまり3×6の値が返されます。

注意
n倍の九九は、固定された数学的規則に基づいています。threeTimesTable[someIndex]を新しい値に設定することは適切ではないため、TimesTableのサブスクリプトは読み取り専用のサブスクリプトとして定義されます。

12.2. サブスクリプトの使用法

「サブスクリプト」の正確な意味は、それが使用される文脈によって異なります。サブスクリプトは通常、コレクション、リスト、またはシーケンスのメンバー要素にアクセスするためのショートカットとして使用されます。特定のクラスまたは構造体の機能に最も適切な方法でサブスクリプトを自由に実装できます。

たとえば、Swiftの辞書型は、辞書インスタンスに格納されている値を設定および取得するためのサブスクリプトを実装します。辞書のキー型のキーをサブスクリプトで囲み、辞書の値型の値をサブスクリプトに割り当てることで、辞書に値を設定できます。

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

上記の例では、numberOfLegsという変数を定義し、3つのキーと値のペアを含む辞書リテラルで初期化します。numberOfLegs辞書の型は[String:Int]であると推測されます。辞書を作成した後、この例ではサブスクリプトの割り当てを使用して、「bird」の文字列キーと2のInt値を辞書に追加します。

辞書のサブスクリプトの詳細については、辞書へのアクセスと変更を参照してください。

注意
Swiftの辞書型は、Key-Valueサブスクリプトを、オプショナル型を受け取って返すサブスクリプトとして実装します。上記のnumberOfLegs辞書の場合、Key-Valueサブスクリプトは、型Int?または「オプショナルのint」の値を受け取り、返します。辞書型は、オプショナルのサブスクリプト型を使用して、すべてのキーに値があるわけではないという事実をモデル化し、キーにnil値を割り当てることによってキーの値を削除する方法を提供します。

12.3. サブスクリプトオプション

サブスクリプトは任意の数の入力パラメータを取ることができ、これらの入力パラメータは任意の型にすることができます。サブスクリプトは、任意の型の値を返すこともできます。

関数と同様に、サブスクリプトは、Variadicパラメータとデフォルトパラメータ値で説明されているように、さまざまな数のパラメータを取り、それらのパラメータのデフォルト値を提供できます。ただし、関数とは異なり、サブスクリプトはin-outパラメータを使用できません。

クラスまたは構造体は、必要な数のサブスクリプト実装を提供できます。使用される適切なサブスクリプトは、サブスクリプトが使用される時点でサブスクリプト括弧内に含まれる1つまたは複数の値の型に基づいて推測されます。複数のサブスクリプトのこの定義は、サブスクリプトのオーバーロードとして知られています。

サブスクリプトが単一のパラメータを取るのが最も一般的ですが、型に適している場合は、複数のパラメータを使用してサブスクリプトを定義することもできます。次の例では、Double値の2次元行列を表す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は、rowsとcolumnsと呼ばれる2つのパラメータを受け取り、Double型のrows * columnsの値を格納するのに十分な大きさの配列を作成する初期化子を提供します。行列の各位置には、0.0の初期値が与えられます。これを実現するために、配列のサイズと初期セル値0.0が、正しいサイズの新しい配列を作成して初期化する配列初期化子に渡されます。この初期化子については、デフォルト値を使用した配列の作成で詳しく説明しています。

適切な行と列の数を初期化子に渡すことで、新しいMatrixインスタンスを作成できます。

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

上記の例では、2行2列の新しいMatrixインスタンスを作成します。このMatrixインスタンスのグリッド配列は、左上から右下に読み取られるように、事実上、マトリックスのフラット化されたバージョンです。

行列の値は、行と列の値をコンマで区切ってサブスクリプトに渡すことで設定できます。

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

これらの2つのステートメントは、サブスクリプトのセッターを呼び出して、マトリックスの右上の位置(rowが0でcolumnが1)に値1.5を設定し、左下の位置(rowが1でcolumnが0)に3.2の値を設定します。

Matrixサブスクリプトのゲッターとセッターの両方に、サブスクリプトのrowとcolumnの値が有効であることを確認するためのアサーションが含まれています。これらのアサーションをサポートするために、MatrixにはindexIsValid(row:column:)という便利なメソッドが含まれています。このメソッドは、要求されたrowとcolumnがMatrixの境界内にあるかどうかをチェックします。

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

マトリックスの境界外にあるサブスクリプトにアクセスしようとすると、アサーションがトリガーされます。

let someValue = matrix[2, 2]
// [2, 2]はマトリックスの境界外にあるため、これによりアサートがトリガーされます

12.4. 型サブスクリプト

上記のように、インスタンスのサブスクリプトは、特定の型のインスタンスで呼び出すサブスクリプトです。型自体で呼び出されるサブスクリプトを定義することもできます。この種のサブスクリプトは、型サブスクリプトと呼ばれます。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)

タイトルとURLをコピーしました