/**
1.泛型所解決的問題
2.泛型函數
3.類型參數
4.命名類型參數
5.泛型類型
6.類型約束
7.關聯類型
8.where語句
泛型代碼可以讓你寫出根據自我需要定義、適用於任何類型的,靈活且可重用的函數和類型。可以避免重複的代碼,用一種清晰和抽象方式來表達代碼的意圖
泛型是swift強大特徵中的其中一個,許多swift標準庫是通過泛型代碼構建出來的。swift的數組和字典類型都是泛型集。可以創建一個Int數組,也可以創建一個String數組,或者甚至於可以是任何其他swift的類型數據數組。同樣的,也可以創建存儲任何指定類型的字典,而且這個類型可以是沒有限制的
*/
//泛型所解決的問題
func swapTwoInts(inout a: Int, inout b: Int) {
let temporaryA = a;
a = b;
b = temporaryA;
}
var someInt = 3;
var anotherInt = 107;
swapTwoInts(&someInt, b: &anotherInt);
print(someInt,anotherInt);
/**
如果要交換其他類型的值,就需要創建更多相同的方法,只是參數類型不一樣而已.
但實際應用中通常需要一個用處更強大並且儘可能的考慮到更多的靈活性單個函數,可以用來交換兩個任何類型值,很幸運的是,泛型代碼幫你解決了這種問題
在所有三個函數中,a和b的類型是一樣的,如果a和b不是相同的類型,那他們兩個就不能互換值。swift是類型安全的語言,所以它不允許一個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) {
let temporaryA = a;
a = b;
b = temporaryA;
}
//泛型函數
/**
這個函數的泛型版本使用了佔位類型名字(通常此情況下用字母T來表示)來代替實際類型名。佔位類型名沒有提示T必須是什麼類型,但是它提示a和b必須是同一類型T,而不管T表示什麼類型,只有swapTwoValues函數在每次調用時所傳入的實際類型才能決定T所代表的類型
*/
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a;
a = b;
b = temporaryA;
}
var someInt2 = 4;
var anotherInt2 = 432;
swapTwoValues(&someInt2, b: &anotherInt2);
print(someInt2, anotherInt2);
var someString = "123";
var anotherString = "456";
swapTwoValues(&someString, b: &anotherString);
print("\(someString) , \(anotherString)");
//類型參數
/**
一旦一個類型參數被指定,那麼其可以被使用來定義一個函數的參數類型,或作爲一個函數返回類型,或用作函數主體中的註釋類型。在這種情況下,被類型參數所代表的佔位類型不管函數任何時候被調用,都會被實際類型所替換,你可支持多個類型參數,命名在尖括號中,用逗號分開
*/
//泛型類型
/**
通常在泛型函數中,swift允許定義自己的泛型類型。這些自定義類、結構體和枚舉作用於任何類型
*/
struct IntStack {//Int型
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();
}
}
/**
T定義了一個名爲“某種類型T”的節點提供給後來用。這種將來類型可以在結構體的定義裏任何地方表示爲'T'。在這種情況下,T在如下三個地方被用作節點:
1.創建一個名爲items的屬性,使用空的T類型值數組對其進行初始化
2.指定一個包含一個參數名爲item的push方法,該參數必須是T類型
3.指定一個pop方法的返回值,該返回值將是一個T類型值
*/
var stackOfStrings = Stack<String>();
stackOfStrings.push("uno");
print(stackOfStrings);
stackOfStrings.push("dos");
print(stackOfStrings);
stackOfStrings.push("tres");
print(stackOfStrings);
stackOfStrings.push("cuatro");
print(stackOfStrings);
let fromTheTop = stackOfStrings.pop();
print(stackOfStrings);
print(fromTheTop);
//類型約束
/**
詳見資料
*/
//類型約束語法
/**
可以寫一個在一個類型參數名後面的類型約束,通過冒號分隔,來作爲類型參數鏈的一部分
func someFunction<T: SomeClass, U:SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
第一個類型參數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, valueToFind: "llama") {
print("The index of llama is \(foundIndex)");
}
/**
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.213,234.2,12.33443,1.0], valueToFind: 234.2);
print(doubleIndex!);
//關聯類型
/**
當定義一個協議時,有的時候聲明一個或多個關聯類型作爲協議定義的一部分是非常有用的。一個關聯類型給定作用於協議部分的類型一個節點(或別名)。作用於關聯類型上實際類型是不需要指定的,直到該協議接受。關聯類型被指定爲typealias關鍵字
*/
//關聯類型行爲
/**
當定義一個協議時,有點時候聲明一個或多個關聯類型作爲協議定義一部分是非常有用的。一個關聯類型給定作用於協議部分的類型一個節點名(或別名)。作用於關聯類型上實際類型是不需要指定的,直到該協議接受。關聯類型被指定爲typealias關鍵字
*/
protocol Container {
typealias ItemType;
mutating func append(item: ItemType);
var count: Int { get };
subscript(i: Int) -> ItemType { get };
}
/**
Container協議定義了三個任何容器必須支持的兼容要求:
1.必須可能通過append方法添加一個新item到容器裏
2.必須可能通過使用count屬性獲取容器裏items的數量,並返回一個Int值
3.必須可能通過容器的Int索引值下標可以檢索到每一個item
這個協議沒有指定容器裏Item是如何存儲的或何種類型是允許的。這個協議只指定三個任何遵循Container類型所必須支持的功能點。一個遵循的類型也可以提供其他額外的功能,只要滿足這三個條件
*/
struct IntStack2: Container {
var items = [Int]();
mutating func push(item:Int) {
items.append(item);
}
mutating func pop() -> Int {
return items.removeLast();
}
typealias ItemType = Int;
mutating func append(item: ItemType) {
self.push(item);
}
var count: Int {
return items.count;
}
subscript(i: Int) -> Int {
return items[i];
}
}
//遵循Container協議的泛型類型
struct Stack2<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 {//佔位類型參數T被用作append方法的item參數和下標的返回類型。swift因此可以推斷出被用作這個特定容器的ItemType的T的合適類型
return items[i];
}
}
//擴展一個存在的類型爲一指定關聯類型
extension Array: Container {
// mutating func push(item: Element) {
// self.append(item);
// }
// mutating func pop() -> Element {
// self.removeLast();
// }
// mutating func append(item: Element) {
// self.push(item);
// }
// var count: Int {
// return self.count;
// }
// subscript(i: Int) -> Element {
// return self[i];
// }
}
//where語句
/**
類型約束確保你定義關聯類型參數的需要和一泛型函數或類型有關聯
對於關聯類型的定義需求也是非常有用的,可以通過這樣去定義where語句作爲一個類型數隊列的一部分。一個where語句使你能夠要求一個關聯類型遵循一個特定的協議,以及(或)那個特定的類型參數和關聯類型可以是相同的。你可以寫一個where語句,通過緊隨放置where關鍵在類型參數隊列後面,其後跟着一個或者多個針對關聯類型的約束,以及(或)一個或多個類型和關聯的等於關係
*/
func allItemsMatch<C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>(someContainer: C1, anotherContainer: C2) -> Bool {
if someContainer.count != anotherContainer.count {
return false;
}
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i]{
return false;
}
}
return true;
}
/**
這個函數用了兩個參數:someContainer和anotherContainer。someContainer參數是類型C1,anotherContainer參數是類型C2。C1和C2是容器的兩個佔位類型參數,決定了這個函數何時被調用。
這個函數的類型參數列緊隨在兩個類型參數需求的後面
1.C1必須遵循Container協議,寫作(C1: Container)
2.C2必須遵循Container協議
3.C1的ItemType同樣是C2的ItemType (寫作C1.ItemType == C2.ItemType)
4.C1的ItemType必須遵循Equatable協議(寫作C1.ItemType: Equatable)
第三個和第四個要求被定義爲一個where語句的一部分,寫在關鍵字where後面,作爲函數類型參數鏈的一部分
*/
var stackOfStrings2 = Stack2<String>();
stackOfStrings.push("uno");
stackOfStrings.push("dos");
stackOfStrings.push("tres");
var arrayOfStrings = ["uno", "dos", "tres"];
if allItemsMatch(stackOfStrings2, anotherContainer: arrayOfStrings) {
print("All items match");
}else {
print("Not all items match");
}