《Advanced Swift》第二章 內建集合類型:讀書筆記 一、數組 二、字典 三、集合 四、Range

        目前見到的編程語言都以某種方式提供了數組、隊列、棧等基礎數據結構。這些數據結構的設計都十分巧妙,在事件、空間兩個維度來說都表現優秀。Swift也提供了數組、字典、集合三種數據結構,這三種數據結構都是基於泛型來實現的,同時也都是值類型,並且基於寫時複製的技術,在各方面都表現優秀。本章,對於這三種技術數據結構進行介紹。
        除了,瞭解這些基礎的數據結構的用法之外,通過閱讀本章,還可以把解決一個問題——什麼時候用C風格的for循環、什麼時候for in、for each、什麼時候用map(還有flatMap等一些列函數)。

一、數組

1.1 數組和可變性

        在Swift當中實用let、var來定義變量的類型:變量、常量。通過此種方式能夠在編譯器角度來控制常量不可能實際上是一個變量。與OC中的NSArry、NSMutableArray相比,這種基於編譯器的常量更加可靠。

1.2 數組索引

        有人聽說Swift會去檢查數組下標越界,因此可能不考慮這個問題。這是一個錯誤的想法,Swift確實會檢查越界,它值保證數組越界的代碼不會實際執行,但是,這個越界卻會產生崩潰。

var mutableFibs = [0, 1, 1, 2, 3, 5]

// for in 循環
for x in mutableFibs {
    print(x)
}

// dropFirst返回的類型是ArraySlice<Int>,其也是數組類型,所以可以進行for in循環
for x in mutableFibs.dropFirst() {
    print(x)
}

// dropFirst返回的類型是ArraySlice<Int>,其也是數組類型,所以可以進行for in循環
for x in mutableFibs.dropLast(3){
    print(x)
}

// 同時獲得下標與元素
for (idx, element) in mutableFibs.enumerated() {
    print("idx=\(idx) element=\(element)")
}

// 獲取指定元素的下標(原書有誤,已經不存在index方法)
if let idx = mutableFibs.firstIndex(where: { (aElement) -> Bool in
    return aElement == 3
}) {
    print("idx=\(idx)")
}

// 取自函數式編程的方法
let fibsString = mutableFibs.map { (aElement) -> String in
    return String(aElement)
}
print(fibsString)

// 取自函數式編程的方法
let elementIsOne = mutableFibs.filter { (aElement) -> Bool in
    return aElement == 1
}
print(elementIsOne)

1.3 數組變形

        map方法引入自函數式編程思想,其與傳統的c語言的for循環類似。那麼,如何選用呢?筆者認爲:如果要對元素組的元素執行某種“變形”操作,則應該選用map。
        與map類似的方法還有很多,它們的普遍特點是:將函數行爲參數化(像map函數,對於數組的循環、調用用戶傳遞的方法這些寫在標準庫裏,而其他用戶關心的僅僅是以參數形式傳遞給map的具體業務代碼)。

var mutableFibs = [0, 1, 1, 2, 3, 5]

let fibsString = mutableFibs.map { (aElement) -> String in
    return String(aElement)
}
print(fibsString) // ["0", "1", "1", "2", "3", "5"]

var fm = mutableFibs.flatMap { (aElement) -> [Int] in
    // 構造一個新數組,每個數組有aElement個元素,每個元素的值與aElement相等
    return Array(repeating: aElement, count: aElement)
}
print(fm) // [1, 1, 2, 2, 3, 3, 3, 5, 5, 5, 5, 5]

let elementIsOne = mutableFibs.filter { (aElement) -> Bool in
    return aElement == 1
}
print(elementIsOne)//[1, 1]

let result = mutableFibs.allSatisfy { (aElement) -> Bool in
    return aElement == 2
}
print(result) // false

let reduceResult = mutableFibs.reduce(0) { (aResult, aElement) -> Int in
    return aResult + aElement
}
print(reduceResult) // 12

mutableFibs.forEach { (aElement) in
    print(aElement)
} // 0, 1, 1, 2, 3, 5

// firstIndex、lastIndex、first、last 這些方法都類似
if let idx = mutableFibs.firstIndex(where: { (aElement) -> Bool in
    return aElement == 3
}) {
    print("idx=\(idx)") // idx=4
}

let min = mutableFibs.min { (first, second) -> Bool in
    first < second
}
print(min) // Optional(0)

let max = mutableFibs.max { (first, second) -> Bool in
    first < second
}
print(max) // Optional(5)


let equal = mutableFibs.elementsEqual([1,2,3])
print(equal) // false

let split = mutableFibs.split { (aElement) -> Bool in
    return aElement > 3
}
print(split) // [ArraySlice([0, 1, 1, 2, 3])]


let prefix = mutableFibs.prefix { (aElement) -> Bool in
    return aElement < 3
}
print(prefix) // [0, 1, 1, 2]


let drop = mutableFibs.drop { (aElement) -> Bool in
    return aElement < 3
}
print(drop) // [3, 5]

mutableFibs.removeAll { (aElement) -> Bool in
    return aElement > 3
}
print(mutableFibs) // [0, 1, 1, 2, 3]

        數組的forEach方法與C裏面的for極其類似,但是forEach更強調的是對各元素都執行某個操作且不會中途返回。另外,forEach適合執行帶有副作用(參見函數式編程中的副作用,簡而言之就是會影響其東西)的循環。

1.4 數組切片

let slice = fibs[1...]
print(slice) // [1, 1, 2, 3, 5]
type(of: slice) // ArraySlice<Int>

print(slice[2], slice[3]) // 1 2

ArraySlice就是數組切片類型,其是符合Sequece協議的一種類型。他與數組的最大區別是:其下標是從獲得切片的原始數組的下標開始。

此書對於高階函數(map、flatMap)講解的不是很詳細,需要詳細詳細瞭解的,請移步:Swift高階函數解析

二、字典

enum Setting {
    case text(String)
    case int(Int)
    case bool(Bool)
}

let defaultSettings: [String:Setting] = ["Airplane Mode": .bool(false),
                                         "Name": .text("My iPhone"),]
print(defaultSettings["Name"]) // Optional(Setting.text("My iPhone"))

        這是一個字典的示例。字典的可變性與數組相同。字典要求其鍵符合Hashable的要求,標準庫提供的所有數據類型都已符合此協議,對於自定義的結構體、枚舉,如果其成員是符合Hashable要求的,那麼其也會有Swift編譯器自動合成出符合Hashable要求的方法。Hashable協議繼承自Equatable。

三、集合

        標準庫中第三種主要的集合類型是集合 Set (雖然聽起來有些彆扭)。集合是一組無序的元素, 每個元素只會出現一次。你可以將集合想像爲一個只存儲了鍵而沒有存儲值的字典。和 Dictionary 一樣,Set 也是通過哈希表實現的,並擁有類似的性能特性和要求。測試集合中是 否包含某個元素是一個常數時間的操作,和字典中的鍵一樣,集合中的元素也必須滿足 Hashable。

let naturals: Set = [1, 2, 3, 2]
print(naturals) // [1, 2, 3]
print(naturals.contains(3)) // true
print(naturals.contains(0)) // false


let iPods: Set = ["iPod touch", "iPod nano", "iPod mini", "iPod shuffle", "iPod Classic"]
let discontinuedIPods: Set = ["iPod mini", "iPod Classic", "iPod nano", "iPod shuffle"]
let currentIPods = iPods.subtracting(discontinuedIPods)
print(currentIPods)// ["iPod touch"]   補集

let touchscreen: Set = ["iPhone", "iPad", "iPod touch", "iPod nano"]
let iPodsWithTouch = iPods.intersection(touchscreen)
print(iPodsWithTouch)// ["iPod nano", "iPod touch"]  交集

var discontinued: Set = ["iBook", "Powerbook", "Power Mac"]
discontinued.formUnion(discontinuedIPods)
print(discontinued)//["iPod shuffle", "iBook", "iPod Classic", "Powerbook", "iPod mini", "iPod nano", "Power Mac"] 並集

        IndexSet、CharacterSet是標準庫提供另外兩個集合。CharacterSet 是一個高效的存儲 Unicode 編碼點 (code point) 的集合。IndexSet 表示了一個由正整數組成的集合。

四、Range

       範圍代表的是兩個值的區間,它由上下邊界進行定義。你可以通過 ..< 來創建一個不包含上邊界 的半開範圍,或者使用 ... 創建同時包含上下邊界的閉合範圍。

// 0 到 9, 不包含 10
let singleDigitNumbers = 0..<10
print(Array(singleDigitNumbers)) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //包含"z"
let lowercaseLetters = Character("a")...Character("z")

let fromZero = 0...
let upToZ = ..<Character("z")

for i in 0..<10 {
print("\(i)", terminator: " ")
} // 0 1 2 3 4 5 6 7 8 9

// 錯誤:'Character' 類型沒有實現 'Strideable' 協議
// 原因是Unicode
for c in lowercaseLetters {
  ...
}
  • 只有半開範圍能表達空間隔 (也就是下界和上界相等的情況,比如 5..<5)。
  • 只有閉合範圍能包括其元素類型所能表達的最大值 (比如 0...Int.max)。而半開範圍則要
    求範圍上界是一個比自身所包含的最大值還要大 1 的值。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章