# Excel 與 VBA 財務應用初階班

---
# 範例 Excel 檔案
請下載:[Lecture 6 - For 巢狀迴圈範例](https://drive.google.com/uc?export=download&id=1T8_jvUTYSZTkrPrKHlIvuJl3WqgKiBR_)
---
## 範例:計算 FANG(牙尖股)的報酬率

---
# 巢狀迴圈(Nested For Loop)
一個 VBA 迴圈一次只能夠遍歷一個方向
由上往下或由左至右迭代
若今天希望能夠同時往兩個方向迭代
就需要巢狀迴圈
---
# 巢狀迴圈(Nested For Loop)
簡單來説,就是在 For 迴圈内再寫一個 For 迴圈
```vb
Dim i as Integer
Dim j as Integer
'外面的迴圈會從 1 到 3
For i = 1 To 3
For j = 6 To 8
'當外面的 For 迭代一次,裏面的 For 會全部都迭代
Debug.print "i = " & i & " j = " & j
Next j
Next i
```
---
# 巢狀迴圈(Nested For Loop)
```sh
i = 1 j = 6
i = 1 j = 7
i = 1 j = 8
i = 2 j = 6
i = 2 j = 7
i = 2 j = 8
i = 3 j = 6
i = 3 j = 7
i = 3 j = 8
```
---
## 巢狀迴圈(Nested For Loop)
接下來我們就可以用巢狀迴圈計算所有股票的報酬率

---
# 第二步:比較每一天MSFT與IBM何者的報酬率高
```vb
Dim i as Integer
For i = 2 To 25
If Cells(i, "C").value > Cells(i, "E").value Then
Cells(i, "F").value = "MSFT"
Else
Cells(i, "F").value = "IBM"
End IF
Next i
```
---
### 範例:比較每一天MSFT與IBM何者的報酬率高

---
# 但是...
我們還漏考慮了一個狀況...
若同一天兩支股票的報酬率都是 0 呢?
---
# 練習:修改判斷式
可能的結果有三種:
1. MSFT 比較高
2. IBM 比較高
3. 兩者都沒有波動
修改原本的程式碼,讓我們的程式能夠正確處理以上三種狀況
---
# 讓使用者選擇要比較的日期
問題是我們缺乏一個良好的界面
---
# 要如何通過使用者輸入的日期去找到欲查詢的日期?
---
# InputBox() 函數
顯示可輸入文字的對話方塊

---
# InputBox() 函數
InputBox() 函數會將使用者的輸入存入等號左邊的變數
```vb
Dim targetDate as Date
targetDate = InputBox("請輸入欲查詢日期:")
```
---
# Cells.Find()
搜尋指定的值所在的儲存格
```vb
Cells.Find(what:=<要查詢的值>, After:=<在哪個儲存格之後>)
```
---
# 練習
利用 `Cells.find()` 查詢 **456**
---
# Cells.Find()
搜尋指定的值所在的儲存格
```vb
'將 targetCell 宣告成一個 Range,能夠儲存一個儲存格物件
Dim targetCell as Range
'賦值時記得需要加上 Set 關鍵字
Set targetCell = Cells.Find(what:=targetDate, After:=Cells(4, "D"))
'檢查是否搜尋到正確的儲存格
Debug.print targetCell.row
Debug.print targetCell.column
```
---
# Cells.find()
但是若找不到指定的值,就會回傳 Nothing
```vb
Sub FindEx()
Dim findCell As Range
Set findCell = Cells.Find(What:=911, After:=Cells(4, "D"))
End Sub
```
---
## Nothing 空值
若使用者沒有輸入任何訊息,InputBox() 會回傳 `Nothing`,也就是 VBA 的空值
----
# TypeName() 函數
檢查一個變數内的資料型別
```vb
Sub FindEx()
Dim findCell As Range
'尋找一個不存在的數字,若 Cells.Find() 找不到便會回傳 Nothing
Set findCell = Cells.Find(What:=911, After:=Cells(4, "D"))
Msgbox(TypeName(findCell))
End Sub
```
就會顯示 `Nothing`
---
### 0 vs Nothing
0 與空值是兩回事,不要搞混了!
0 是表示有無
空值是用來代表**不存在**的意思

---
# 錯誤處理
爲了防範 Nothing 往下執行破壞我們的程式,我們需要防錯機制
---
# Exit Sub
强制推出目前的 `Sub` 子程序
```vb
Sub ExitSubEx()
Msgbox("我會被執行哦!")
Exit Sub
Msgbox("我不會被執行哦!")
End Sub
```
---
# Exit Sub
修改原本的程式碼,加入 `Exit Sub` 避免程式碼出錯
```vb
Sub FindEx()
Dim findCell As Range
Dim targetDate As Date
Set findCell = Cells.Find(What:=targetDate, After:=Cells(4, "D"))
If findCell Is Nothing Then
MsgBox ("找不到值哦,啾咪!")
Exit Sub
End If
End Sub
```
---
# On Error Goto
---
# On Error Goto
```vb
On Error Goto <標籤名稱>
一段有可能出錯的程式碼
標籤:
若出錯了,就跳到這邊,不往下執行
```
---
# On Error Goto
```vb
Sub FindEx()
Dim findCell As Range
Dim targetRow as Integer
Dim targetCol as Integer
'要是 Cells.Find 出錯,就跳到 FindFail
On Error Goto FindFail
Set findCell = Cells.Find(What:=911, After:=Cells(4, "D"))
targetRow = findCell.Row
targetCol = findCell.Column
Debug.print "cell.row =" & targetRow
Exit Sub
FindFail:
Msgbox("找不到指定的值!")
End Sub
```
---
# 更多關於 On Error Goto
官方文件:[連結](https://docs.microsoft.com/zh-tw/office/vba/language/reference/user-interface-help/on-error-statement)
---
# 判斷走勢
現在我們再打造另一個功能:判斷 MSFT 與 IBM 兩支股票的走勢是否相同

---
# 我們的邏輯
寫成判斷式非常可怕...
```vb
If (msftReturn > 0 And ibmReturn > 0) Or (msftReturn < 0 And ibmReturn < 0) Or (msftReturn = 0 And ibmReturn = 0) Then ...
```
---
# Sgn() 函數
判斷一個數字是正數、負數、或是0
若正數,則回傳 1
若負數,則回傳 -1
若是零,則回傳 0
---
# 練習
利用 Sgn() 簡化判斷式
---
## 回家作業
請下載:[Lecture 6 - 稅率計算機](https://drive.google.com/uc?export=download&id=15xuZOvuBsRUpFemwssqh_SuKjR7zZwWM)
---
根據範例Excel檔的資料,計算第一年的月薪數目
```vb
Sub GetMonthlySalary()
End Sub
```
---
## 回家作業
根據範例Excel檔的資料,計算每一年的月薪數目
```vb
Sub GetYearlySalary()
End Sub
```
---
## 回家作業
根據範例Excel檔的資料,計算每一年的年薪數目
```vb
Sub TotalIncome
End Sub
```
---
## 回家作業
根據範例Excel檔的資料,計算一輩子的收入數目
```vb
Sub GetLifeSalary()
End Sub
```
---
# For Each 迴圈
將一個儲存格範圍内所有的**儲存格物件**迭代一次
順序是固定從**上往下,由左至右**
```vb
'宣告變數的型別為一個 range
Dim cell As Range
'迭代從 A1 到 C3 的儲存格
For Each cell In Range("A1:C3")
Debug.Print cell.Value
Next cell
```
---
# While 迴圈
另一種常用的迴圈
```vb
While <條件式>
'若條件式成立就繼續執行...
Wend
```
---
# 範例
寫一支 VBA 程式,用 While 迴圈將 1 至 100 的加總算出來
```vb
Dim sum As Integer: sum = 1
Dim count As Integer: count = 1
'若 count 還沒大於等於 100 就繼續迭代
While count <= 100
'每迭代一次就將 sum 加上 count 值
sum = sum + count
'每迭代一次就將 count 加上 1
count = count + 1
Wend
Debug.print "sum: " & sum
```
---
# Do While 迴圈
```vb
Dim count As Integer: count = 1
Do While count <= 10
count = count + 1
Debug.print count
Loop
```
---
# 當你在寫 While 迴圈時
## 請想好你的結束迴圈的機制!
## 請想好你的結束迴圈的機制!
## 請想好你的結束迴圈的機制!
*很重要所以說三次
---
# Exit Do 提前結束迴圈
```vb
Dim count As Integer: count = 1
'讓程式一直執行
Do While True
count = count + 1
Msgbox(count)
If count = 10 Then
Exit Do
End If
Loop
Msgbox("我在迴圈外面!")
```
---
# For vs While
當你很明確的知道迴圈需要執行幾次,使用 For Loop
當你不確定迴圈需要執行幾次,使用 While Loop