//***********************************************************************************************
//1.Generics(泛型)
//_______________________________________________________________________________________________
//介绍
//泛型代码可以让你写出根据自我需求定义,适用于任何类型,灵活并且可以重用的函数和类型。它可以避免重复的代码,用一种清晰和抽象的方式来表达代码的意图
//泛型是 Swift强大特征中的一个,许多 Swift 标准库都是通过泛型代码构建出来的。
//***********************************************************************************************
//2.The Problem That Generics Solve(泛型所解决的问题)
//_______________________________________________________________________________________________
//代码演示非泛型函数 swapTwoInts,用来交换两个 Int值
func swapTwoInts(inout a:Int, inout b:Int){ //这个函数使用 inout来交换 a和 b的值
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now\(someInt),and anotherInt is now\(anotherInt)") //此时我们思考,swapTwoInt 函数非常有用,但是它只交换 Int 值,如果我们想交换两个 String或者 Double,就不得不写更多的函数
func swapTwoStrings(inout a:String, inout b:String) { //书写交换两个字符串的方法
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoDoubles(inout a:Double, inout b: Double) { //书写交换两个 Double 类型浮点数的方法,这时我们发现只有参数不同,函数结构都相同,使得代码变得庞大重复,这时我们可以使用泛型
let temporaryA = a
a = b
b = temporaryA
}
//***********************************************************************************************
//3.Generic Functions(泛型函数)
//_______________________________________________________________________________________________
//介绍
//泛型函数可以工作与任何类型(实例代码)
func swapTwoValues<T>(inout a:T, inout b:T){ //使用泛型来优化代码
let temporaryA = a
a = b
b = temporaryA
}
var someInt1 = 4 //整形数据测试泛型
var anotherInt1 = 108
swapTwoValues(&someInt1, &anotherInt1)
println("someInt1 is now\(someInt1),and another is now\(anotherInt)")
var someString = "Hello"
var anotherString = "world" //字符串测试泛型
swapTwoValues(&someString, &anotherString)
println("someString is now\(someString),and another is now\(anotherString)")
//***********************************************************************************************
//4.Type Parameters(类型参数)
//_______________________________________________________________________________________________
//介绍
//类型参数指定并命名为一个占位类型,并且紧随在函数的后面,使用一对尖括号括起来
//***********************************************************************************************
//5.Naming Type Parameters(命名类型参数)
//_______________________________________________________________________________________________
//类型参数的命名规则使用首字母大写,并且驼峰式命名
//***********************************************************************************************
//6.Generic Types(泛型类型)
//_______________________________________________________________________________________________
//介绍
//通常在泛型函数中,Swift允许我么你自定义自己的泛型类型,这些自定义类,结构体和枚举作用于任何类型,如同 Array和 Dictionary 的用法
//栈,一个栈是一系列值域的集合,和 Array类似,但其是一个比 Swift 的 Array类型更多限制的集合。一个数组允许其里面的任何位置插入删除数据,而栈,只允许在集合的末端添加新的项,同样也只能在集合的末尾删除项
//_______________________________________________________________________________________________
//实例代码演示写一个非泛型版本的栈,Int值型的栈
struct IntStack{
var items = [Int]()
mutating func push(item:Int){ //向数组存入元素到末尾
items.append(item)
}
mutating func pop() ->Int{
return items.removeLast() //移除末尾的元素
}
}
//_______________________________________________________________________________________________
//实例代码演示写一个泛型版本的栈
struct Stack<T> { //泛型版本的类型紧跟着结构体名的后面 <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("Hello")
stackOfString.push("dos")
println(stackOfString.items)
stackOfString.pop()
println(stackOfString.items)
//***********************************************************************************************
//7.Type Constraints(类型约束)
//_______________________________________________________________________________________________
//介绍
//swapTwoValues 函数和 Stack类型可以作用于任何类型,不过有的时候在使用泛型函数和泛型类型上的类型强制约束为某种特定类型是非常必要的,这就是类型约束
//_______________________________________________________________________________________________
//类型约束语法
/*
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { //假定函数有两个类型参数。第一个类型参数 T,有一个需要 T必须是 SomeClass 子类的类型约束;第二个类型参数 U,有一个需要 U必须遵循 SomeProtocol 协议的类型 约束
// function body goes here
}
*/
//_______________________________________________________________________________________________
//类型约束行为(非泛型函数的类型约束行为)
func findStringIndex(array: [String], valueToFind:String) -> Int?{
for (index, value) in enumerate(array){ //遍历索引固定字符串的下标
if value == valueToFind{
return index
}
}
return nil
}
let strings = ["cat","dog", "llama","parakeet"]
if let foundIndex = findStringIndex(strings,"llama"){
println("the index of llama is\(foundIndex)")
}
//_______________________________________________________________________________________________
//类型约束行为(泛型函数的类型约束行为)
func findIndex<T: Equatable>(array: [T], valueToFind:T) -> Int? { //这里的 <T: Equatable> 表示一种安全的形式,因为并不是所有的数据都可以用 == 来判断相等的,如果一个结构体或者类很复杂,系统无法判断等于的含义,所以如果不写 Equatable协议,程序无法运行,此时Equatable 协议在这里充当了对参数类型 T 的类型约束,约束它可以被使用 ==运算
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
let doubleIndex = findIndex([3.14159,0.1, 2.3], 9.3)
println(doubleIndex)
//***********************************************************************************************
//8.Associated Types(关联类型)
//_______________________________________________________________________________________________
//介绍
//当定义一个协议的时候,有的时候声明一个或者多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个别名。作用于关联类型上实际类型不需要指定,直到该协议接受,关联类型被指定为 typealias关键字
//_______________________________________________________________________________________________
//实例代码演示关联类型行为(非泛型代码)
protocol Container{ //定义 Container协议
typealias ItemType //定义 ItemType关联类型
mutating func append(item:ItemType) //必须可能通过 append 方法添加一个新 item 到容器里
var count:Int {get} //必须可能通过使用 count 属性获取容器里 items 的数量,并返回一个 Int值
subscript(i:Int) -> ItemType {get} //必须可能通过容器的 Int 索引值下标可以检索到每一个 item
}
struct IntStacks:Container{
var items = [Int]()
mutating func push(item:Int){
items.append(item)
}
mutating func pop() ->Int {
return items.removeLast()
}
typealias ItemType = Int //将抽象的 typealias 类型定义为 Int 类型,类型可以自动判断,所以代码也可以删除
mutating func append(item:Int){
self.push(item)
}
var count: Int{
return items.count
}
subscript (i: Int) ->Int{
return items[i]
}
}
//_______________________________________________________________________________________________
//泛型代码演示关联类型行为
struct Stacks<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]
}
}
//_______________________________________________________________________________________________
//扩展一个存在的类型为一个指定关联类型
extension Array:Container{} //定义一个扩展,因为 Array 默认已经实现了 Container 协议中的所有功能,所以我们可以将任何 Array当作 Container 来使用
//***********************************************************************************************
//9.Where Clauses(where 语句)
//_______________________________________________________________________________________________
//介绍
//类型约束中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联
//_______________________________________________________________________________________________
//实例代码演示 where语句的用法
func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, anotherContainer:C2) -> Bool { //这个函数用了两个参数:someContainer和 anotherContainer。someContainer参数是类型 C1,anotherContainer 参数是类型 C2
/*
参数链解析
C1 必须遵循 Container协议 (写作 C1: Container)。
C2 必须遵循 Container协议 (写作 C2: Container)。
C1 的 ItemType 同样是 C2 的 ItemType(写作 C1.ItemType == C2.ItemType)。
C1 的 ItemType 必须遵循 Equatable 协议 (写作 C1.ItemType: Equatable)
*/
if someContainer.count != anotherContainer.count {
return false
}
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
var stackOfStrings = Stacks<String>()
stackOfStrings.push("Listo")
stackOfStrings.push("Pin")
stackOfStrings.push("Melody")
var arrayOfString1 = ["Listo","Pin", "Melody"]
if allItemsMatch(stackOfStrings,arrayOfString1){
println("All items match")
}
else{
println("Not all items match")
}