サブスクリプト(Subscripts)

最終更新日: 2021/6/28
原文: https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html

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

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

サブスクリプト構文(Subscript Syntax)

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

subscript(index: Int) -> Int {
    get {
        // ここで適切な値を返します
    }
    set(newValue) {
        // ここで適切な値を設定するアクションをします
    }
}

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

読み取り専用の計算プロパティと同様に、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

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

threeTimesTable[6] で示されているように、サブスクリプトを呼び出すことで threeTimesTable インスタンスの検索を実行できます。これは、3 の倍数テーブルの 6 番目のエントリを要求し、値 18、つまり 3 x 6 を返します。

NOTE
n 倍数テーブル は、決まった数学ルールに基づいています。threeTimesTable[someIndex] に新しい値に設定することは適切ではないため、TimesTable のサブスクリプトは読み取り専用として定義されています。

サブスクリプトの利用(Subscript Usage)

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

例えば、Swift の Dictionary 型は、Dictionary インスタンスに格納されているバリューを設定および取得するためのサブスクリプトを実装します。辞書にバリューを設定するには、辞書のキーの型をサブスクリプトの角括弧([])で囲み、辞書のバリューの型の値をサブスクリプトに割り当てます:

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

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

Dictionary のサブスクリプトの詳細については、Accessing and Modifying a Dictionary(辞書へのアクセスと変更)を参照ください。

NOTE
Swift の Dictionary 型は、オプショナルの型を受け取って返すサブスクリプトとしてキーバリューのサブスクリプトを実装します。上記の numberOfLegs 辞書の場合、キーとバリューのサブスクリプトは Int? または オプショナル Int 型の値を受け取って返します。Dictionary 型は、 オプショナルのサブスクリプトの型を使用して、全てのキーにバリューがあるわけではないということをモデル化し、そのキーに nil 値を割り当てることによってキーに対応したバリューを削除する方法を提供します。

様々なサブスクリプト(Subscript Options)

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

関数と同様に、サブスクリプトは様々な数のパラメータを受け取り、パラメータのデフォルト値を提供できます。これについては、Variadic Parameters(可変長パラメータ)Default Parameter Values(デフォルトパラメータ値)で説明しています。ただし、関数とは異なり、サブスクリプトは inout パラメータを使用できません。

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

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

適切な rows 数と columns 数をイニシャライザに渡すことで、新しい Matrix インスタンスを構築できます:

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

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

フラット化されたマトリックス

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

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

これらの 2 つの文は、サブスクリプトの set を呼び出して、行列の右上の位置(row は 0、column は 1) に 1.5 の値を設定し、左下の位置 (row1column0) に 3.2 の値を設定します。

行列の右上と左下の位置に値を設定

Matrix のサブスクリプトの get/set には、サブスクリプトの rowcolumn の値が有効だということを確認するためのアサーションが含まれています。これらのアサーションを支援するために、Matrix には indexIsValid(row:column:) という便利なメソッドが含まれています。これは、要求された rowcolumn が行列の境界内にあるかどうかをチェックします:

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

行列の境界の外にあるサブスクリプトにアクセスしようとすると、実行時エラーが発生します。

let someValue = matrix[2, 2]
// [2, 2] は、行列の境界の外にあるので実行時エラーが発生します

型サブスクリプト(Type Subscripts)

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

results matching ""

    No results matching ""