/*泛型代码可以让你写出根据自我需求定义、适用于任何类型的,灵活且可重用的函数和类型。它的可以让你避免重复的代码,
用一种清晰和抽象的方式来表达代码的意图。
数组和字典都是泛型。(可以创建任意类型的数组及字典)
*/
/*泛型函数
函数名后跟占位符<T>,尖括号告诉Swift那个T是函数定义的一个类型,因T时一个占位命名类型,Swift不会去查找命名为T的实际类型。
函数每次被调用时传入的参数的实际类型才能决定T所代表的类型。
*/
func swapTwoValues<T>(inout a: T, inout _ b: T) {
let tempA = a
a = b
b = tempA
}
//T为Int类型
var someInt = 3
var anotherInt = 100
swap(&someInt, &anotherInt)
print("someInt: \(someInt), anotherInt: \(anotherInt)")
//someInt: 100, anotherInt: 3
//T为String类型
var someString = "hello"
var anotherString = "world"
swap(&someString, &anotherString)
print("someString: \(someString), anotherString: \(anotherString)")
//someString: world, anotherString: hello
/*类型参数
占位符T是一种类型参数,一旦类型参数被指定,那么可以用来定义一个函数的参数类型,或作为一个函数的返回类型,
或作为函数主题中的注释类型,在被调用的时候替换为实际类型。
可以支持多个参数类型,命名在尖括号中,用逗号分开。
命名类型参数
通常用一单个字母T来命名类型参数,不过可以用任何有效的标识符来作为类型参数名(首字母大写的驼峰式命名)
如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。
例如,Swift 字典(Dictionary)类型有两个类型参数,一个是键,另外一个是值。
*/
/*泛型类型
通常在泛型函数中,Swift 允许你定义你自己的泛型类型。这些自定义类、结构体和枚举作用于任何类型,如同Array和Dictionary的用法。
简记:泛型可用用于自定义类,结构体和枚举
*/
//非泛型栈
struct IntStack {
var items = [Int]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
//泛型栈
struct Stack<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
//创建一个字符串类型的栈
var stackOfString = Stack<String>()
stackOfString.push("uno")
stackOfString.push("dos")
stackOfString.push("tres")
stackOfString.push("cuatro")
let fromTheTop = stackOfString.pop()
print("fromTheTop: \(fromTheTop)")
/*扩展一个泛型类型
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,
原始类型定义中声明的类型参数列表在扩展里是可以使用的,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
*/
extension Stack {
//返回T类型的可选值
var topItem: T? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
if let topItem = stackOfString.topItem {
print("The top item on the stack is \(topItem).")
}
//The top item on the stack is tres.
/*类型约束
你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。
这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这里是函数主体
}
上面这个假定函数有两个类型参数。第一个类型参数T,有一个需要T必须是SomeClass子类的类型约束;第二个类型参数U,
有一个需要U必须遵循SomeProtocol协议的类型约束。
*/
func findStringIndex(array: [String], _ valueToFind:String) -> Int? {
for (index, value) in array.enumerate() {
if value == valueToFind {
return index
}
}
return nil
}
let strings = ["cat", "dog", "llama","parakeet","terrapin"]
if let foundIndex = findStringIndex(strings, "llama") {
print("The index of llama is \(foundIndex)")
}
//he index of llama is 2
//泛型版本
//参数类(T)型遵循 Equatable(等式)协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持Equatable协议。
func findIndex<T:Equatable>(array:[T], _ valueToFind:T) -> Int? {
for (index, value) in array.enumerate() {
if value == valueToFind {
return index
}
}
return nil
}
let doubleIndex = findIndex([3.14159,0.1,0.25], 9.3)
let stringIndex = findIndex(["mike","Malcolm","Andrea"], "Andrea")
print("doubleIndex:\(doubleIndex), stringIndex:\(stringIndex!)")
//doubleIndex:nil, stringIndex:2
/*关联类型(Associated Types)
当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型作为协议的一部分,
给定了类型的一个占位名(或别名)。作用于关联类型上实际类型在协议被实现前是不需要指定的。关联类型被指定为typealias关键字。
*/
protocol Container {
//定义关联类型
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
//遵循Container协议的Int型栈
struct IntStack1: Container {
var items = [Int]()
mutating func push(item:Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
//遵循Container协议实现
typealias ItemType = Int //这行可以省略
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
// 遵循Container协议的泛型栈
struct Stack1<T>: Container {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
mutating func append(item: T) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> T {
return items[i]
}
}
var ss1 = Stack1<String>()
ss1.append("wo")
ss1.append("ni")
print("\(ss1[0])")//wo
/*扩展一个存在的类型为一指定关联类型
在在扩展中添加协议成员中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。
*/
//可以把Array当作Container来使用
extension Array: Container {}
/*where 语句
类型约束能够确保类型符合泛型函数或类的定义约束。
对关联类型定义约束是非常有用的。你可以在参数列表中通过where语句定义参数的约束。
一个where语句能够使一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。
你可以写一个where语句,紧跟在在类型参数列表后面,where语句后跟一个或者多个针对关联类型的约束,
以及(或)一个或多个类型和关联类型间的等价(equality)关系。
下例:
C1必须遵循Container协议 (写作 C1: Container)。
C2必须遵循Container协议 (写作 C2: Container)。
C1的ItemType同样是C2的ItemType(写作 C1.ItemType == C2.ItemType)。
C1的ItemType必须遵循Equatable协议 (写作 C1.ItemType: Equatable)。
*/
func allItemsMatch<
C1:Container, C2:Container
where C1.ItemType == C2.ItemType, C1.ItemType:Equatable>
(someContainer: C1, _ anotherContainer: C2) -> Bool {
//判断两个Container中元素数量是否相等
if someContainer.count != anotherContainer.count {
return false
}
// 判断两个Container相同位置的元素彼此是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
var stackOfStrings1 = Stack1<String>()
stackOfStrings1.push("uno")
stackOfStrings1.push("dos")
stackOfStrings1.push("tres")
//前面定义Array已经遵循Container协议
var arrayOfStrings = ["uno", "dos", "tres"]
if allItemsMatch(stackOfStrings1, arrayOfStrings) {
print("All items match.")
} else {
print("Not all items match.")
}
//All items match.