Array
и Dictionary
оба являются шаблонными коллекциями. Вы можете создать массив, который содержит значения Int
или массив значений String
, или массив для любого другого типа, который может быть создан в Swift. Аналогично, Вы можете создать словарь для хранения любого конкретного типа, и нет никаких ограничений на то, каким должен быть этот тип.
swapTwoInts(_:_:)
, которая меняет два значения Int
:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
Эта функция использует in-out параметры для взаимного обмена значений a и b.
swapTwoInts(_:_:)
заменяет изначальное значение b на a, а значение a на b. Вы можете вызвать эту функцию для замены двух значений типа Int
:
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Выведет "someInt is now 107, and anotherInt is now 3"
swapTwoInts(_:_:)
полезна, но она может быть использована только со значениями Int
. Если Вы хотите обменять два значения типа String
или Double
, Вам нужно будет написать большей функцией, как например swapTwoStrings(_:_:)
и swapTwoDoubles(_:_:)
, как показано ниже:
func swapTwoStrings(_ a: inout String, _ b: inout String) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
let temporaryA = a
a = b
b = temporaryA
}
Вы могли заметить, что тела функций swapTwoInts(_:_:)
, swapTwoStrings(_:_:)
и swapTwoDoubles(_:_:)
идентичны. Единственная разница между ними состоит в типе значений, ими принимаемых (Int, String и Double).
String
и Double
обменяться значениями. Попытка сделать это приведёт к ошибке времени компиляции.
swapTwoInts(_:_:)
из примера выше с названием swapTwoValues(_:_:)
:
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoInts(_ a: inout Int, _ b: inout Int)
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
Тело функции swapTwoValues(_:_:)
идентично телу функции swapTwoInts(_:_:)
. Однако, первая строчка swapTwoValues(_:_:)
существенно отличается от swapTwoInts(_:_:)
. Здесь представлен пример сравнения первых строчек:
Int
, String
или Double
). Замещаемое имя типа ничего не говорит о том, каким должен быть этот тип T, но оно говорит о том, что оба объекта a и b должны быть одного типа T, каким бы не был T. Настоящее значение типа T используется вместо T и определяется при всяком вызове функции swapTwoValues(_:_:)
.
(swapTwoValues(_:_:))
записывается вместе с замещающим именем типа (T) внутри угловых скобок (<T>. Угловые скобки говорят Swift, что T - это замещающее имя типа внутри определения функции (swapTwoValues(_:_:)). Так как T - это плейсхолдер, то Swift не ищет тип с названием T.
swapTwoValues(_:_:)
теперь может быть вызвана также как swapTwoInts
за исключением того, что она может принять два значения любого типа, если оба этих значения имеют один и тот же тип. Всякий раз при вызове swapTwoValues(_:_:)
тип T выводится из типа переданных в функцию величин.
Int
и String
соответственно:
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"
Функция swapTwoValues(_:_:)
, определённая выше, создана на основе шаблонной функции с названием swap
, которая является частью стандартной библиотеки Swift и автоматически делается доступной для использования в Ваших приложениях. Если Вам нужно поведение функции swapTwoValues(_:_:)
в Вашем собственном коде, то Вы можете использовать существующую функцию swap(_:_:)
вместо предоставления Вашей собственной реализации.
swapTwoValues(_:_:)
выше плейсхолдер типа T - это пример параметра типа. Параметры типов определяют и именуют замещаемый тип и записываются сразу после имени функции между парой угловых скобок (как например swapTwoValues(_:_:)
) или возвращаемого значения функции или в качестве аннотации типа внутри тела функции. В любом случае, параметр типа заменяется настоящим типом при каждом вызове функции. (В примере swapTwoValues(_:_:)
выше T заменяется Int
при первом вызове функции и String
- при втором.)
Key
и Value
в Dictionary<Key
, Value>
и Element
в Array<Element>, которые говорят читателю о взаимоотношениях между типом параметра и шаблонным типом или функцией, в которых он используется. Однако, когда между ними нет значащего соотношения, то их традиционно называют одой буквой наподобие T, U и V, как например T в функции swapTwoValues(_:_:)
выше.
MyTypeParameter
) для указания на то, что они являются плейсхолдерами для типа, а не значения.
Array
и Dictionary
.
Stack
. Стэк - это сортированный набор значений, аналогичный массиве, но с более ограниченным набором операций, чем имеет тип Array
в Swift. Массив позволяет новым элементам быть вставленными и удалёнными на любой позиции в массиве. Стэк, однако, позволяет новым элементам быть добавленными только к концу коллекции (известно как впихивание нового значения в стэк). Аналогично, стэк позволяет новым элементам быть удалёнными только с конца коллекции (что известно как выпихивание значения из стэка).
Int
значений:
struct IntStack {
var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
Эта структура использует свойство типа Array
с названием items
для хранения значений в стэке. Stack
предоставляйте два метода, push
и pop
, для вставки и извлечения значений в и из стэка. Эти методы маркированы как mutating
, так как им нужно модифицировать (или мутировать) массив структуры items
.
IntStack
, показанный выше, может хранить только значения Int
, однако. Это было бы более полезно определить шаблонный класс Stack
, который может управлять стэком любого типа значений.
struct Stack {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
Заметьте, что шаблонная версия Stack
фактически совпадает с нешаблонной, но она имеет параметр типа с названием Element
вместо конкретного типа Int
. Этот параметр типа записывается внутри пары угловых скобок (<Element> сразу после имени структуры.
Element
определяет замещаемое имя для типа, который будет предоставлен позднее. Этот будущее тип может быть использован внутри любого места определения структуры. В этом случае Element
используется в качестве плейсхолдера в трёх местах:
Stack
может быть использован для создания стока любого корректного типа в Swift, как это происходит с типами Array
и Dictionary
.
Stack
, написав тип хранимого в стэке/ внутри угловых скобок. Например, для создания нового стэка/ строк, Вы можете написать Stack<String>():
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
// the stack now contains 4 strings
stackOfStrings
после вставки этих четырёх значений в него:
let fromTheTop = stackOfStrings.pop()
// fromTheTop is equal to "cuatro", and the stack now contains 3 strings
Stack
для добавления вычисляемого свойства только-для-чтения с названием topItem
, которое возвращает вершинное значение на стэке без его извлечения:
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
Свойство topItem
возвращает опциональное значение типа Element
. Если стэк пусть, то topItem
вернёт nil
, если стэк не пуст, то topItem
вернёт последний элемент из массива array
.
Stack
имя параметра Element
используется внутри расширения для указания на опциональный тип вычисляемого свойства topItem
.
topItem
теперь может быть использовано у любого экземпляра типа Stack
для доступа и запроса на его вершинный элемент без его изъятия.
if let topItem = stackOfStrings.topItem {
print("The top item on the stack is \(topItem).")
}
// Выведет "The top item on the stack is tres."
Расширения шаблонного типа могут также включать требования на то, что объекты расширяемого типа должны им удовлетворять для того, чтобы получить новую функциональность.