---
title: 'Swift - 判斷、集合、迴圈'
disqus: kyleAlien
---
Swift - 判斷、集合、迴圈
===
## Overview of Content
[TOC]
## Swift 表達結構
在這裡我們要建立幾個名詞的概念,陳述式、表達式、變數、常數... 等等,它們都與程式密切的關係
:::info
這些名詞可以讓我們更好的分析、認識程式,但注意不要糾結於這些名詞,這些名詞可能會因為語言不同而有不同的描述方式
:::
### Swift 程式 - 基本組件
* Swift 程式是由各種不同元素組成,其主要組成元素如下(請配合以下範例一起對照)
| 元素名 | 特性 | 補充 |
| - | - | - |
| 變數(variable) | 變數是一種可以在運行時改變的數據 | - |
| 常數(constant) | 常數在運行時不可修改 | 設定後就永遠不可修改 |
| 字面值(literal) | 字面值就是有意義的 “數據”,這些數據有各種不同型態(String, Bool... 等等) | - |
| 型態(type) | 標註變數、常數的類型,說明要如何解釋這個數據 | - |
| 陳述式(statement) | 程式準備執行的動作 | for-in, if... 等等,會有許多不同特性 |
| 表達式(expression) | 負責回傳結果值 | - |
| 宣告(declaration) | 負責將一個新的名字(使用者定義)導入程式 | 可以宣告變數、常數、函數、結構 ... 等等 |
```swift=
// *變數*
// 變數格式:var <變數名> : <型態> = <字面值>
// 對照:變數名 myString
// 型態 String
// 字面值 "Hello world"
var myString : String = "Hello world"
// *變數*
var isShowing : Bool = true
// *常數*
// 常數格式:var <變數名> : <型態> = <字面值>
// 對照:變數名 NAME
// 型態 String(語字自動推導)
// 字面值 "Hello world"
let NAME = "Kyle"
// *陳述式*(判斷)
if isShowing {
// *陳述式*(循環迴圈)
for tmp in 1...5 {
// 函數
print("\(myString): \(tmp)")
}
}
// *表達式* (最終輸出一個結果)
myString += ", Yoyo"
// 函數
print("\(NAME), \(myString)")
```
> 執行結果
>
> 
### 運算子 Operator
* 建構組件 運算子 (Operator) 在每個程式語言中都相當重要,可以透過它檢查、修改資料;它的通常有兩種方式表達
* **符號**:`+`、`-`、`*`... 等等
* **片語**:`inc`、`dec` ... 等等(通常用於運算符重載,每個運算符都有對應的片語,可以參考 )
> 這裡不說明片語
* 運算子分類:如下表
| 分類 | 說明 | 舉例 |
| -------- | -------- | -------- |
| 一元 (unary) | 運算一個數值 | -1 |
| 二元 (binary) | 運算兩個值 | 1 + 2 |
| 三元 (ternary) | 先判斷再決定 | value ? 1 : 666 |
:::info
* 運算子運作的 Scope
上面範例來說都是數字運算,然而運算子不只可以運作在數字上 (只是常見);它還可以運作在字串,甚至 **只要符合某些條件也可以運作在物件上**
:::
* 下表為幾個常見的數學運算子
| 運算子 | 說明 | 舉例 |
| - | - | - |
| + | 加法 | 1 + 2 = 3 |
| - | 減法 | 10 - 8 = 2 |
| * | 乘法 | 11 * 3 = 33 |
| / | 除法 | 6 / 3 = 2 |
| % | 取模 (取餘數) | 10 / 7 = 3 |
```swift=
print("1 + 2 = \(1 + 2)")
print("10 - 8 = \(10 - 8)")
print("11 * 3 = \(11 * 3)")
print("6 / 3 = \(6 / 3)")
print("10 % 7 = \(10 % 7)")
```
> 
:::success
* **表達式(expression)**
運算子最終都會產生一個值,對於有最終產值的行為,我們都稱為表達式(expression)
:::
:::warning
* **空格不可混用**
Swift 中空格與運算子之間不可混用否則會編譯不過
```swift=
var result = 0
// Error
result = 12 /4
result = 12/ 4
// OK
result = 12 / 4
result = 12/4
```
:::
* 等號 `=` 也是運算子(**指定運算子**),所有出現在 `=` 後的值都是 **字面值(`literal`)**,這個字面值不只是數字,也可以是其他數據
### Swift 基礎型態
* **++型態++** 對於程式語言是一種強硬的設計!但同時也保證其安全性;在 Swift 中有以下方式可以定義型態
1. 宣告變數時,直接指定型態
```swift=
var myVariable1: Int = 123
```
2. 宣告變數時,給定數值,讓編譯器自動推導
```swift=
var myVariable2 = 77.0
```
3. 宣告變數,並指定型態,延遲指定變數
```swift=
var myVariable3: String
myVariable3 = "iOS"
```
:::danger
* 延遲指定注意
1. 這裡雖然延遲指定變數,但是在使用該變數時編譯器會判斷你是否有指定變數,如果沒指定會導致編譯不過
> 
2. 延遲指定變數,之後要指定也要是相同類型才能指定
> 
:::
* Swift 的基礎型態包括:
> Swift 還提供了許多高級型態,例如陣列、字典、元組等,可以更方便地處理複雜的數據結構
1. **整數型態**:用於表示整數數值,包括 `Int`、`UInt8`、`UInt16`、`UInt32`、`UInt64`、`Int8`、`Int16`、`Int32`、`Int64`
```swift=
var myInt: Int = 10
```
:::info
* **Int 怎麼分這麼多不同型態**?
我們最常用的 `Int` 型態會依據我們使用裝置的硬體(CPU 總線)來決定該型態的大小(可能 32 or 64 bit)
而其他型態則是我們清楚指明要使用的類型,其可能包括有符號 (Signed)、無符號 (Unsignd)的不同大小
:::
2. **浮點型態**:用於表示浮點數值,包括 `Float`、`Double`;**swift 預設型態為 Double**
> 我們可以透過 type 來判斷其類型
```kotlin=
var myFloat : Float = 10.0
print(type(of: myFloat))
var myDobule : Double = 10.0
print(type(of: myDobule))
print("Default: \(type(of: 10.0))")
```
> 
3. **Bool 布林型態**:用於表示邏輯值,包括 `true` 和 `false`
```swift=
var myBool : Bool = true
var myBool_1 : Bool = false
```
4. **String 字符串型態**:用於表示文字,包括 `String`
```swift=
var myString: String = "Hello World"
```
5. **Character 字符型態**:用於表示單個字符,包括 `Character`;Swift 不使用這個符號 `'` 描述單個字符
```swift=
var myChar : Character = "A"
```
6. **選擇型態**:用於表示一個值或者沒有值(也就是可以沒有值的意思),包括 `Optional`
```swift=
var myOptional : Optional = "ABC"
// 非 Optional 型態不可設定為 nil
myOptional = nil
```
> Optional 型態的細節之後再說明
## 集合
Swift 有一系列用於儲存數據的型態(類型),這些型態就稱為 **集合**,其主要集合型態為 ^1.^ Array, ^2.^ Set, ^3.^ Dictionary 它們的特性都不相同
:::warning
集合中可以有多個數據,但 **集合中的數據必須要相同類型**
:::
### Array - 順序性
* **Array(陣列)的特性**:**有順序性、數據可以重複、透索引值取值**;其創建方式如下
> Array 又稱之為陣列
:::success
索引值從 0 開始算
:::
* 數據格式:[ <數據>* ]
```swift=
let myArray : Array = [1, 2, 9, 3, 7, 9]
// 其型態也可以寫出來
let myArray2 : [Int] = [1, 2, 9, 3, 7, 9]
```
* **Array 常用 API**:
| 功能 | API |
| - | - |
| Array 當前數據數量 | count |
| Array 是否為空 | isEmpty |
| 新數據加到後方 | append(value) |
| 新數據插入指定位置 | insert(value, index) |
| 移除指定位置數據 | remove(index) |
```swift=
var myArray : [Int] = [1, 2, 3]
print("index of 0: \(myArray[0])")
myArray.append(100)
print("index of 3: \(myArray[3])")
myArray.insert(99, at: 3)
print("index of 3: \(myArray[3])")
if !myArray.isEmpty {
print("Current count, \(myArray.count)")
}
print("Before remove, index of 0: \(myArray[0])")
myArray.remove(at: 0)
print("After remove, index of 0: \(myArray[0])")
```
> 
### Set - 不可重複性
* **Set 的特性**:**無序性、數據唯一、無法透過索引取值**;其創建方式如下
* 數據格式:
1. Set([ <數據>* ])
```swift=
let mySet1 = Set([3, 3, 1, 4, 7, 9, 1])
```
2. 宣告類型 Set = [ <數據>* ]
```swift=
let mySet2 : Set = [3, 3, 1, 4, 7, 9, 1]
```
* **Set 常用 API**:
| API | 功能 |
|:---------------| ------------------------ |
| count | Array 當前數據數量 |
| isEmpty | Array 是否為空 |
| insert(value) | 插入新數據 |
| contains(value) | 判斷集合內是否有某個數據 |
| remove(index) | 移除指定數據 |
```swift=
var mySet : Set = [9, 2, 3, 5, 5, 6, 1, 5]
mySet.insert(8)
if mySet.contains(9) {
mySet.remove(9)
}
if !mySet.isEmpty {
print("Current count, \(mySet.count)")
for item in mySet {
print("Set item: \(item)")
}
}
```
:::warning
1. 從結果可以發現,Set 不會儲存重複相同的數據
> 以上 5 雖然重複,不過它最終在 Set 中只有一個
2. Set 集合是無法直接取得數據的,所以這邊使用 for 循環遍歷集合
:::
> 
### Dictionary - 鍵值
* **Dictionary 的特性**:**無序性、數據透過 key/Value 的方式儲存**;其創建方式如下
* 數據格式:[ <Key : Value\>* ]
```swift=
let myDictionary = [ 1 : "A", 2 : "B", 3 : "C", 4 : "D" ]
let myDictionary2: [Int: String] = [ 1 : "A", 2 : "B", 3 : "C", 4 : "D" ]
```
:::info
* 可以指定一個空 Dictionary,不過要先宣告類型,否則編譯器會不知道該變數的類型
```swift=
let emptyDictionary : [String: Float] = [:]
```
:::
* **Dictionary 常用 API**:
| 功能 | API |
| - | - |
| count | Dictionary 當前數據數量 |
| isEmpty | Dictionary 是否為空 |
| updateValue(Value, Key) | 更新指定 Key 的數據 |
| [Key] = Value | 更新指定 Key 的數據 |
| removeValue(key) | 用 Key 來移除 Dictionary 中的數據 |
```swift=
var myDictionary3 = [ 1 : "A", 2 : "B", 3 : "C", 4 : "D" ]
if !myDictionary3.isEmpty {
print("Count: \(myDictionary.count)")
}
myDictionary3.updateValue("AAA", forKey: 1)
print("The 1 value is \(myDictionary3[1])")
myDictionary3[1] = "ABCDEFG"
print("The 1 value is \(myDictionary3[1])")
myDictionary3.removeValue(forKey: 2)
print("The 2 is \(myDictionary3[2])")
```
> 
### Tuple 集合
* Tuple (元組) 集合可以讓多個值儲存在單一變數、常數中,其中的元素可以修改,而不會像有陣列之類的負擔
> 並且它可以特別指定元素的名稱
:::info
* 這裡的負擔是指啥?
* Array 通常需要指定大小或容量,並且需要連續的記憶體空間來存儲元素。當陣列的大小需要改變時,可能需要重新分配記憶體空間,把現有的元素拷貝到新的位置上,這樣的操作需要額外的時間和資源
* 相較之下,Tuple 可以讓多個值以 **有序** 的方式存在單一變數、常數中,^1.^ 不需要指定容量,也 ^2.^ 不需要連續的記憶體空間
因此,使用元組可以減少記憶體的使用,並且在運行時不需要額外的內存分配和拷貝操作,因此可以更加高效
:::
* Tuple 可以指定 & 不指定其元素的名稱
```swift=
var myTuple = (10, 20)
// tuple 元素可修改
myTuple.0 = 55
// 不指定時按照 index 取得數據
print("my tuple , 0: \(myTuple.0)")
print("my tuple , 1: \(myTuple.1)")
// 可以指定元素名稱
var myTuple2 = (a: 100, 200)
// 按照元素名稱取得數據
print("my tuple 2, a: \(myTuple2.a)")
// 沒指定的話,就按照 index 取得
print("my tuple 2, 1: \(myTuple2.1)")
```
> 
## 陳述式 - Expression
上面我們有提到陳述式(Expression)是一個程式語言的基本組成,它可以幫你進行迴圈、判斷... 等等功能;
:::success
這種控制程式運行的手法常被稱之為:控制流、結構化
:::
* swift 常用 Expression 如下
1. 判斷條件:`if`, `switch`
2. 迴圈:`for`, `for-in`, `while` ,`repeat-while`
### 判斷條件 - if
* 使用 `if` 的前提是,真假判斷(Bool 表達式)也就是說要有一個 True、False 的輸出,才能使用 `if` 判斷;其格式如下
```swift=
if(真假判斷) {
// 成立後執行的程式
} else {
// 不成立後執行的程式
}
// 使用空格就可以省略 `()`---------------------------
if 真假判斷 {
// 成立後執行的程式
} else {
// 不成立後執行的程式
}
```
* 使用範例:
```swift=
var showMsg: Bool = true
if showMsg {
print("Hello World")
} else {
print("What?")
}
```
> 
* 同一個 `if` 判斷,可以用 `else if` 增加更多的條件判斷,也可以讓其他人明白該判斷相關的條件不止一個
```swift=
var mylist = [1, 2, 3]
if mylist.isEmpty {
print("I get empty list.")
} else if mylist.count == 1 {
print("I get the list, the size is 1.")
} else if mylist.count == 2 {
print("I get the list, the size is 2.")
} else if mylist.count == 3 {
print("I get the list, the size is 3.")
} else {
print("I get the big size of list.")
}
```
> 
### 判斷條件 - switch
* 使用 `switch` 同樣是判斷條件,但是它會把自動把需要判斷的數據帶入每個不同的條件狀況 (`case`),看起來會更加的簡潔;其格式如下
```swift=
switch(數值) {
case 條件狀況:
// 符合條件狀況則執行這
default:
// 以上狀況皆不符合則執行這
}
// 使用空格就 switch 可以省略 `()`---------------------------
switch 數值 {
case 條件狀況:
// 符合條件狀況則執行這
default:
// 以上狀況皆不符合則執行這
}
```
:::success
* `if` vs. `switch` 使用
兩者沒有絕對要使用哪一個(基本上憑經驗判斷),但可以注意到的是 `if` 可以取多種方案多個判斷,而 `switch` 更針對一種方案多個判斷
> switch 更指定,長遠來看更好維護
:::
* 使用範例:
```swift=
var mylist = [1, 2]
// 指定要判斷的方案
switch mylist.count {
// 以下在各種 case 中判斷
case 0:
print("I get empty list.")
case 1:
print("I get the list, the size is 1.")
case 2:
print("I get the list, the size is 2.")
case 3:
print("I get the list, the size is 3.")
default:
print("I get the big size of list.")
}
```
> 
:::warning
* **Swift 與其他語言的不同**:
它仍然有 `break`、`continue`,不過在 case 自然結束時不需要添加,只有在需要中途跳出時才需添加
```swift=
switch mylist.count {
case 0:
print("I get empty list.")
case 1, 2:
if mylist[0] == 1 {
print("list element of 0 if 1")
// 跳出該 case
break
}
// 不會被執行到
print("I get the list!")
default:
print("I get the big size of list.")
}
```
> 
:::
### 迴圈 - for-in / foreach
* `for-in` 陳述式用來遍歷集合(Collection)數據,其格式如下
```swift=
// 遍歷 range 時,`constains` 數值就會一直更新
for constains in range {
// other code
}
```
* `for-in` 使用範例
```swift=
let array = [1, 2, 55, 11, 88]
for tmp in array {
print("Current value = \(tmp)")
}
```
> 
:::info
* `foreach` 是集合的一種 API 可以簡化 `for-in`,使用方式如下:
```swift=
let array = [1, 2, 55, 11, 88]
array.forEach {
// 取直改成使用 `$0`
print("Current value = \($0)")
}
```
> 輸出同上
:::
:::success
* swift 沒辦法自己控制回圈的 index,所以要逆向搜尋的話就需要使用 `resversed()` 方法
```swift=
for j in (0...times).reversed() {
// do nothing
}
```
:::
### 迴圈 - while / repeat-while
* `while` 陳述式同樣可以用來遍歷數據,通常用使用在不清楚回圈需循環幾次時的狀況;其格式如下
```swift=
// 每次循環都會回頭判斷 condition
// 若 condition 為 false 則結束循環,反知繼續循環
while condition {
// other code
}
```
* `while` 使用範例
```swift=
var value = 1
while value < 100 {
print("Before value: \(value)")
value = value * 5
print("After value: \(value)\n")
}
print("Final value: \(value)")
```
> 
* `repeat-while` 與 `while` 差不多,但是 `repeat-while` 的判斷放置到執行程式之後;其格式如下
```swift=
var value = 1
var updating = true
repeat {
if value < 100 {
print("Before value: \(value)")
value = value * 5
print("After value: \(value)\n")
} else {
updating = false
}
} while updating
print("Final value: \(value)")
```
:::warning
* `while`, `repeat-while` 使用起來要特別注意,若是沒有修改條件(condition)則會造成無限迴圈
:::
## 其他
### 型態別名 - typealias
* 首先,型態別名(type alias)只會對該型態取一個別名,並不會修改該型態的特性,**它可以加強程式的可讀性!** 其格式如下
```swift=
typealias <別名> = <型態>
```
* 範例:
```swift=
typealias Celsuis = Int
typealias Fahrenheit = Int
var tempC : Celsuis = 35
var tempF : Fahrenheit
tempF = (tempC * 9 / 5) + 32
print("Celsuis = \(tempC), Fahrenheit = \(tempF)")
```
> 
:::warning
* 過多的別名可能會導致程式的可讀性降低,最好是將別名限制在某個區域範圍內使用
:::
### 範疇運算子
* Swift 有提供幾個運算子(符號)來幫助我們簡單定義一組範圍;見下表
| 名稱 | 運算子(符號格式) | 重點 |
| - | - | - |
| 封閉範疇運算子 | num_1...num_2 | 預設 `<=` (左開又開) |
| 半開放範圍運算子 | num_1..<num_2 | 指定 `<` (左開右閉) |
| 單邊範圍運算子 | num_1... | 從 >= num_1 開始不限定最大 |
| 單邊範圍運算子 | ...num_2 | 不限最最小直到 <= num_2 |
```swift=
let hasLess = 10...
print("hasLess, contains 10: \(hasLess.contains(10))")
print("hasLess, contains 11: \(hasLess.contains(11))")
let hasMax = ...100
print("hasMax, contains 100: \(hasMax.contains(100))")
print("hasMax, contains 101: \(hasMax.contains(101))")
for i in 3...5 {
print("hasRange, item: \(i)")
}
let range = 10..<20
print("range, contains 19: \(range.contains(19))")
print("range, contains 20: \(range.contains(20))")
```
> 
* 範疇運算子也可以用在 switch 陳述式中
```swift=
let value = 19
switch value {
case 0...10:
print("In range 0...10")
case 10...20:
print("In range 10...20")
default:
print("Value in other range.")
}
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `iOS`