Сабскрипты

Классы, структуры и перечисления могут определять сабскрипты, которые являются сокращенным вариантом доступа к члену коллекции, списка или последовательности. Вы можете использовать сабскрипт для получения или установки нового значения элемента без разделения этих двух методов (получения значения и установкой нового). К примеру, вы можете воспользоваться сабскриптом в экземпляре массива для получения значения элемента someArray[index] или в экземпляре словаря someDictionary[key].

17 ноября 2022

 

Вы можете определить несколько сабскриптов для одного типа, при необходимости загружается подходящий сабскрипт, который выбирается в зависимости от типа значения сабскрипта, который вы передаете в сабскрипт. Сабскрипты не ограничены одной размерностью, вы можете определить сабскрипты с множественными вводами параметров для удовлетворения потребностей вашего пользовательского типа.

Синтаксис сабскрипта

Сабскрипты позволяют вам запрашивать экземпляры определенного типа, написав одно или несколько значений в квадратных скобках после имени экземпляра. Синтаксис сабскрипта аналогичный синтаксису метода экземпляра и вычисляемому свойству. Вы пишете определения сабскрипта с помощью ключевого слова subscript и указываете один или более входных параметров и возвращаемый тип, точно так же как и в методах экземпляра. В отличии от методов экземпляра, сабскрипты могут быть read-write или read-only. Такое поведение сообщается геттером и сеттером в точности так же как и в вычисляемых свойствах:


subscript(index: Int) -> Int {
    get {
        //возвращает надлежащее значение скрипта
    }
    set(newValue) {
        //проводит надлежащие установки 
    }
}

Тип newValue такой же как и у возвращаемого значения сабскрипта. В случае с вычисляемыми свойствами вы можете не указывать параметр сеттера (newValue). Параметр по умолчанию называется newValue и предоставляется, если не было назначено другого.

Как и в случае с нередактируемыми (read-only) вычисляемыми свойствами, вы можете опустить слово get для нередактируемых (read-only) сабскриптов:


subscript(index: Int) -> Int {
   //возвращает надлежащее значение скрипта
}

Пример определения нередактируемого сабскрипта, который определяет структуру TimesTable показать таблицу умножения на n:


struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
 
let threeTimesTable = TimesTable(multiplier: 3)
print("шесть умножить на три будет \(threeTimesTable[6])")
// Выведет "шесть умножить на три будет 18"

В этом примере новый экземпляр TimesTable создан для отображения таблицы умножения на три. Это определяется переданным ему значением 3 в инициализатор структуры как значение для параметра экземпляра multiplier.

Вы можете запросить экземпляр threeTimesTable с помощью сабскрипта, как уже сделано в примере выше threeTimesTable[6]. Мы запрашиваем шестую запись в таблице умножения на три, которая возвращает значение 18 или 6 умноженное на 3.

Заметка

Таблица умножения на n основана на фиксированном математическом правиле. Присваивание нового значения threeTimesTable[someIndex] не подходит для нашего варианта, значит сабскрипт TimesTable определен как неизменяемый сабскрипт.

Использование сабскрипта

Точное значение “сабскрипта” зависит от контекста, в котором он применяется. Обычно сабскрипты используются в качестве сокращенного способа обращения к элементу коллекции, списка или последовательности. Вы свободны применять сабскрипты в необходимой форме для вашего класса или для функциональности структуры.

К примеру, словарь в языке Swift использует сабскрипт для присваивания или получения значения, которое хранится в экземпляре Dictionary. Вы можете задать значение в словаре, и используя ключ типа ключа словаря в квадратных скобках, присваивая значение типа словаря через сабскрипт:


var numberOfLegs = ["паук": 8, "муравей": 6, "кошка": 4]
numberOfLegs["птичка"] = 2

В примере выше мы объявляем переменную numberOfLegs и инициализируем ее с помощью литерала словаря, который содержит три пары ключ-значение. Тип словаря numberOfLegs выводится как [String: Int]. После того как словарь создан, в этом примере используется сабскриптное присваивание для добавления ключа типа String “птичка“, значения типа Int 2.

Для более подробного описания работы со словарями обратитесь к разделу Доступ и изменение словаря.

Заметка

Тип Dictionary в Swift осуществляет ключ-значение индексирование, как сабскрипт, который получает опциональное значение. Для словаря numberOfLegs, сабскрипт ключ-значение берет и возвращает значение типа Int? или “опциональный Int”. Тип Dictionary использует опциональный тип сабскрипта, чтобы смоделировать факт того, что не каждый ключ может иметь значение, и для того, чтобы была возможность удаления значения для ключа, присваивая ему nil.

Опции сабскрипта

Сабскрипты могут принимать любое количество входных параметров, и эти параметры могут быть любого типа. Сабскрипты так же могут возвращать любой тип.

Как и функции сабскрипты могут принимать различное число параметров и предоставлять исходные значения для этих параметров, как обсуждалось в "Вариативные параметры" и "Значения по умолчанию для параметров". Однако в отличие от функций, сабскрипты не могут использовать сквозные параметры.

Класс или структура могут обеспечить столько сабскриптных реализаций, сколько нужно, и подходящий сабскрипт, который будет использоваться, будет выведен, основываясь на типе значения или значений, которые содержатся внутри скобок сабскрипта, в том месте, где этот сабскрипт используется. Определение множественных сабскриптов так же известно как перегрузка сабскрипта.

Сейчас в большинстве случаев сабскрипт принимает один единственный параметр, вы так же можете определить сабскрипт с несколькими параметрами, если этот вариант подходит для вашего типа. Следующий пример определяет структуру Matrix, которая представляет собой двухмерную матрицу значений типа Double. Сабскрипт структуры Matrix принимает два целочисленных параметра.


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, и создает массив типа Double, который имеет размер rows * columns. Каждой позиции в матрице дается начальное значение 0.0. Чтобы этого достичь, размер массива и начальное значение клетки равное 0.0 передаются в инициализатор массива, который создает и инициализирует новый массив необходимого размера. Это описано подробнее в главе Создание массива с дефолтным значением.

Вы можете создать новый экземпляр типа Matrix, передав количество рядов и столбцов в его инициализатор:


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

Этот пример создает новый экземпляр Matrix, который имеет всего два ряда и два столбца. Массив grid для экземпляра Matrix фактически упрощенный вариант версии матрицы, который читается с левой верхней части в правую нижнюю часть:

Значения в матрице могут быть установлены через передачу значений ряда и столбца в сабскрипте, разделенных между собой запятой:


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

Эти два выражения в сеттере сабскрипта устанавливают значения 1.5 для верхней правой позиции (где row равен 0, column равен 1), и значение 3.2 для нижней левой позиции (где row равен 1, а column равен 0 ):

Геттер и сеттер сабскрипта Matrix оба содержат утверждения для проверки валидности значений row и column. Для помощи утверждениям, Matrix имеет удобный метод под названием indexIsValid(row:column:), который проверяет наличие запрашиваемых row, column в существующей матрице:


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

Утверждения срабатывают, если вы пытаетесь получить доступ к сабскрипту, который находится за пределами матрицы:


let someValue = matrix[2, 2]
//это вызывает утверждение, потому что [2, 2] находится за пределами матрицы

Сабскрипты типа

Сабскрипты сущностей, как было сказано выше, являются сабскриптами экземпляров конкретного типа. Вы также можете определить сабскрипты, которые вызываются у самого типа. Сабскриты такого типа называются сабскриптом типа. Вы указываете сабскрипт типа при помощи ключевого слова static перед ключевым словом subscript. Классы могут использовать ключевое слово class вместо static, чтобы позволить подклассам переопределять реализацию родительского класса этого сабскрипта. Пример ниже показывает, как вы можете определить и вызывать сабскрипт типа:


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)
Содержание