# Lesson 2 | 條件判斷與迴圈
## 第一節:熟悉資料型態(1)
在撰寫條件判斷與迴圈之前,我們先正式介紹一下Python裡面常用的內建型態(Built-in Types)。
* 布林(Boolean):
* True, False
* 數字類型(Numeric Types):
* int, float, complex
* 文本序列類型(Text sequence Type):
* str
* 序列類型(Sequence Types):
* list, tuple, range
* 集合類型(Set Types):
* set
* 映射類型(Mapping Types):
* dict
## 第一節:熟悉資料型態(2)
* 先不要被一大堆定義給嚇到,我們可以將資料型態粗略分為數字跟文字。而以下這些常用的基本函式相當重要:
* type() - 用來查詢變數的資料型態。
* XXX() - XXX為資料型態的名稱,用來創建或轉換資料型態。
* 首先來體驗一下"單個"的資料,我們可以使用對應到資料型態的函式來創建變數。
* bool
```python=
a = True
print('type of a: ', type(a))
b = bool(True)
print('type of b: ', type(b))
```
* int
```python=
c = 16
print('type of c: ', type(c))
d = int(16)
print('type of d: ', type(d))
```
* float
```python=
e = 5.
print('type of e: ', type(e))
f = float(5.3)
print('type of f: ', type(f))
```
* complex
```python=
g = 1+5j
print('type of g: ', type(g))
h = complex(1, 5)
print('type of h: ', type(h))
```
* str
```python=
i = 'ABCD'
print('type of i: ', type(i))
j = str('ABCD')
print('type of j: ', type(j))
k = str('1234')
print('type of k: ', type(k))
```
* 創建資料型態的函式也可以用來轉換資料型態。
* 舉例來說,文本可以被轉換成數字。
```python=
str_2_int = int('9452')
print(str_2_int)
print('type of str_2_int: ', type(str_2_int))
str_2_float = float('9452.087')
print(str_2_float)
print('type of str_2_float: ', type(str_2_float))
```
* 數字間int與float的轉換。
```python=
float_2_int = int(9452.087)
print(float_2_int)
print("type of float_2_int: ", type(float_2_int))
```
## 第一節:熟悉資料型態(3)
* 接下來我們嘗試可以儲存"多個變數"的資料型態。
* list
```python=
list_a = [1, 2, 3, 4, "5"]
print("type of list_a: ", type(list_a))
```
* tuple - 可視為不可修改內部元素的list。
```python=
tuple_a = (1, 2, 3, 4, "5")
print("type of tuple_a: ", type(tuple_a))
tuple_b = tuple([1, 2, 3, 4, "5"])
print("type of tuple_b: ", type(tuple_b))
tuple_a[0] = 1
```
* set - 能讓輸入的元素變成"唯一"。
```python=
set_a = {1, 2, 3, 4, 4}
print(set_a)
print("type of set_a: ", type(set_a))
set_b = set([1, 87, 87, 2, 3])
print(set_b)
print("type of set_b: ", type(set_b))
```
* dict - 可視為能為內部元素命名的list。
```python=
dict_a = {'a': 1, 'b': 2}
print("type of dict_a: ", type(dict_a))
dict_b = dict(a = 1, b = 2)
print("type of dict_b: ", type(dict_b))
```
* 而以下這些基本函式常在這個層級上使用:
* len() - 用來查詢該變數的長度,變數數目。
```python=
list_a = [1, 2, 3, 4, "5"]
len(list_a)
```
* range() - 則是一個比較特別的資料型態,我們常用它來創造數字序列。
```python=
range_10 = range(10)
print("type of range_10: ", type(range_10))
print(list(range_10))
```
## 練習1:熟悉函數組合
* 請各位幫我查看一下這個變數。
```python=
x = [16, 6, 13, 1, 3, 4, 14, 11, 11, 10, 9, 4, 6, 3, 9, 4, 2, 14, 17, 13, 16,
9, 6, 16, 1, 1, 9, 5, 8, 6, 14, 11, 13, 17, 3, 9, 20, 9, 11, 4, 17, 8, 3,
16, 8, 4, 8, 9, 20, 14, 14, 5, 14, 6, 4, 6, 6, 13, 1, 9, 6, 14, 16, 9, 20,
16, 9, 13, 13, 4, 2, 5, 8, 14, 18, 16, 16, 4, 18, 11, 18, 11, 10, 6, 5, 17,
9, 11, 13, 20, 8, 8, 16, 9, 13, 4, 3, 17, 16, 9, 6, 8, 3, 8, 2, 20, 11, 14,
16, 8, 11, 13, 1, 4, 20, 16, 17, 3, 16, 5, 20, 20, 5, 4, 5, 16, 6, 11, 10,
2, 17, 13, 18, 2, 13, 2, 9, 2, 9, 13, 16, 8, 5, 11, 6, 18, 9, 9, 5, 11, 1,
8, 5, 2, 9, 16, 1, 3, 5, 6, 2, 1, 9, 16, 20, 6, 4, 6, 8, 3, 5, 13, 17, 6, 1,
6, 4, 9, 6, 6, 5, 5, 5, 17, 8, 13]
```
* 問題1:在變數「x」內有共幾「個」數字?
<details>
<summary>解答</summary>
- 答案為186。
```python=
print(len(x))
```
</details>
* 問題2:共有幾「種」數字在變數「x」內?
<details>
<summary>解答</summary>
- 我們可以利用set(),來讓list內的元素變成唯一,答案為16。
```python=
print(len(set(x)))
```
</details>
* 問題3:這個數列中缺少了1至20中的哪些數字?
<details>
<summary>解答</summary>
- 這題比較特別,我們可以用肉眼觀察,答案為7、12、15、19。倘若內容物的數字數以百計呢?就需要用到接下來這堂課要介紹的『條件判斷與迴圈』。
```python=
print(set(x))
```
</details>
## 第二節:條件判斷式(1)
* 在Python裡面,要做判斷,常用這幾種「運算」,來回傳布林。
* 布林運算(Boolean Operations):「or」、「and」、「not」、「in」。
```python=
x = True
y = False
# "or" - 或
print(x or y)
# "and" - 且
print(x and y)
# "not" - 不是
print(not x)
# "in" - 在不在裡面
print(x in [False, False, False, False])
```
* 比較運算(Comparisons Operations):「<」、「<=」、「>」、「>=」、「==」、 「!=」。
```python=
print(1 < 2)
print(2 == 1 + 1)
print("Z" > "B")
print("同婚" != "性解放")
```
* 我們可以利用這些「運算」的組合,來做更複雜的判斷。
```python=
x = 9
print(x <= 4 and x > 2)
print(x > 4 or x <= 2)
```
## 第二節:條件判斷式(2)
* 讓我們來正式介紹條件判斷式,使用在複合語句(Compound Statements)當中的「if」。當「if」後為真時,Python會執行冒號後的程式碼。
* 若A,若A則B。
```python=
if "Z" > "B":
x = 2
if "利" > "弊":
y = 3
else:
y = 4
print(x * y)
```
* 若不是則。
```python=
score = 99
if score >= 90:
print('A')
elif score >= 75: # else if
print('B')
elif score >= 60: # else if
print('C')
else:
print('Not pass.')
```
## 練習2:熟悉條件判斷式
* 我們現在已經可以利用我們所學,建構簡單的應用腳本。請同學們參考[國民健康署網站](https://www.hpa.gov.tw/Pages/Detail.aspx?nodeid=542&pid=705),建立一個BMI指數計算器,以變數儲存身高與體重,讓Python回傳其BMI數值以及體重是否正常,回傳的文字包含"體重過輕"、"健康體重"、"體重過重"或是"肥胖"。
<details>
<summary>解答</summary>
```python=
height = 163
weight = 60
BMI = (weight / ((height/100)**2))
print("你輸入的BMI為", BMI, " (kg/m^2).")
if BMI < 18.5:
result = '體重過輕'
elif BMI < 24:
result = '健康體重'
elif BMI < 27:
result = '體重過重'
else:
result = '肥胖'
print("體重狀況: ", result)
```
</details>
## 第二節:迴圈(1)
* 如果我們要對大量的資料進行重複的計算或判斷呢?
* 讓我們觀察「費波納奇數列」:起始數列為1、1,之後每個數為前兩個數之和。
```python=
# 這是一個常用來複製list的方式
x = [0] * 12
# 將前兩個數字皆替換成1。
x[0:2] = 1, 1
x[2] = x[1] + x[0]
x[3] = x[2] + x[1]
x[4] = x[3] + x[2]
x[5] = x[4] + x[3]
x[6] = x[5] + x[4]
x[7] = x[6] + x[5]
x[8] = x[7] + x[6]
x[9] = x[8] + x[7]
x[10] = x[9] + x[8]
x[11] = x[10] + x[9]
print(x)
```
* 你是否覺得這樣的運算存在規律?而要怎樣抽解這些規律呢?
* 在上述程式碼我們注意到了一件事情,也就是每一行程式碼中索引的位置都會一起增加1。我們現在將使用另一個變數來替換程式碼中共同的部分。
```python=
# 創建一個長度為12,元素皆為0的list。
x = [0] * 12
# 將前兩個數字皆替換成1。
x[0:2] = 1, 1
idx = 2
x[idx] = x[idx - 1] + x[idx - 2]
idx = 3
x[idx] = x[idx - 1] + x[idx - 2]
idx = 4
x[idx] = x[idx - 1] + x[idx - 2]
idx = 5
x[idx] = x[idx - 1] + x[idx - 2]
print(x)
```
* 若能定義好變數,讓變數依序改變,我們即可讓程式進行大量運算,這是迴圈的基本邏輯!
## 第二節:迴圈(2)
* 讓我們正式介紹第二個常用的複合語句:「for」,「for」會搭配另外一個運算:「in」使用。
* 我們可以把for迴圈翻譯成"讓...在...裡面,使其從頭到尾依序指派..."。
* 這個時候先不要急,我們先藉由這些下列程式碼來理解迴圈。
* 先宣告一個list來存idx,然後我們使用「for」,在for迴圈內會依序指派「in」後面的變數。
```python=
idx_list = list(range(10))
print(idx_list)
for idx in idx_list:
print(idx)
```
* 我們也可以直接使用range(),range這個資料型態能直接用於迴圈。
```python=
for idx in range(10):
print(idx)
```
* 讓我們回到「費波納奇數列」,我們先宣告物件,並使用迴圈進行運算。
```python=
len_of_num = 12
# 創建長度為12,元素皆為0的list。
x = [0] * len_of_num
# 將前兩個數字皆替換成1。
x[0:2] = 1, 1
print(x)
# 使用迴圈更替idx,並依序執行程式碼。
for idx in range(2, len_of_num):
x[idx] = x[idx - 1] + x[idx - 2]
print(x)
```
## 第二節:迴圈(3)
* 上述程式碼的實際運作,其實是依序重複執行迴圈內的程式碼
* 因此在第一個迴圈中Python其實是做了以下的事情。
* 在第一圈時,變數idx被指定為以range()所產生的第一個內容「2」。
```python=
idx = 2
x[idx] = x[idx - 1] + x[idx - 2]
```
* 第二迴圈則是令idx為range中的第二個數字3。
```python=
idx = 3
x[idx] = x[idx - 1] + x[idx - 2]
```
* 依此類推,到最後一個迴圈時則是令idx = 11。
```python=
idx = 11
x[idx] = x[idx - 1] + x[idx - 2]
```
* 同學們是否覺得迴圈其實很簡單呢?
## 第二節:迴圈(4)
* 迴圈所指派的變數不見得必須是數字,例如也可以是文本,下面是一個合併迴圈與判斷的範例。
* 我們可以讓Python告訴我們這些單字有哪些英文字母的字數大於等於5。
```python=
words = ['cat', 'window', 'defenestrate']
for w in words:
if len(w) >= 5:
print(w, len(w))
```
* 以下範例也是一種常用來索引內容物以及處理資料的演算法。
```python=
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
print(i, a[i])
```
## 練習3:迴圈與條件判斷的組合
* 現在我們已經有更好的方式可以回答本堂課練習1中的問題3。
```python=
x = [16, 6, 13, 1, 3, 4, 14, 11, 11, 10, 9, 4, 6, 3, 9, 4, 2, 14, 17, 13, 16,
9, 6, 16, 1, 1, 9, 5, 8, 6, 14, 11, 13, 17, 3, 9, 20, 9, 11, 4, 17, 8, 3,
16, 8, 4, 8, 9, 20, 14, 14, 5, 14, 6, 4, 6, 6, 13, 1, 9, 6, 14, 16, 9, 20,
16, 9, 13, 13, 4, 2, 5, 8, 14, 18, 16, 16, 4, 18, 11, 18, 11, 10, 6, 5, 17,
9, 11, 13, 20, 8, 8, 16, 9, 13, 4, 3, 17, 16, 9, 6, 8, 3, 8, 2, 20, 11, 14,
16, 8, 11, 13, 1, 4, 20, 16, 17, 3, 16, 5, 20, 20, 5, 4, 5, 16, 6, 11, 10,
2, 17, 13, 18, 2, 13, 2, 9, 2, 9, 13, 16, 8, 5, 11, 6, 18, 9, 9, 5, 11, 1,
8, 5, 2, 9, 16, 1, 3, 5, 6, 2, 1, 9, 16, 20, 6, 4, 6, 8, 3, 5, 13, 17, 6, 1,
6, 4, 9, 6, 6, 5, 5, 5, 17, 8, 13]
```
* 問題1:這個數列中缺少了1至20中的哪些數字?請利用迴圈與判斷。
<details>
<summary>解答</summary>
```python=
uni_x = set(x)
for idx in list(range(1, 21)):
if idx not in uni_x:
print("缺少了", idx)
```
</details>
* 費波納奇數列是起始數列為1、1,之後每個數為前兩個數之和。現在,我們來創造一個NDMC數列,其定義是起始數列為1、1,之後第三個數字(奇數)為前兩個相加,第四個數字(偶數)為前兩個數字相乘,第五個數字(奇數)為前兩個相加,依此類推
```python=
# 創建長度為10,元素皆為0的list。
len_of_num = 10
x = [0] * len_of_num
# 將前兩個數字皆替換成1。
x[0:2] = 1, 1
print(x)
```
* 問題2:請用迴圈功能完成它(小心奇偶數順序時,要做的動作不同,請同學思考如何利用條件判斷來完成這個問題)。
<details>
<summary>解答</summary>
- 這題其實有很多解法,解法一相對直觀。
```python=
for idx in range(2, len_of_num):
if idx % 2 == 0:
x[idx] = x[idx - 1] + x[idx - 2]
else:
x[idx] = x[idx - 1] * x[idx - 2]
print(x)
```
- 解法二相對不直觀,但也是一種思考模式。
```python=
for idx in range(1, int(len_of_num/2)):
x[2*idx] = x[2*idx - 1] + x[2*idx - 2]
x[2*idx+1] = x[2*idx] * x[2*idx - 1]
print(x)
```
</details>
## 第三節:巢狀迴圈(1)
迴圈的美妙之處在於能夠大量且依序運算,我們將透過迴圈進行更複雜的組合任務:巢狀迴圈(Nested for-loop)。
* 現在讓我們想辦法列出2到6內所有的「質數」。
* 何謂質數?質數為找不到比自己更小的可整除除數,如5以下的數字為2、3、4,而5無法被這些數字整除。
* 現在讓我們思考該如何回答這個的問題。
## 第三節:巢狀迴圈(2)
* 我們先產生一個變數x儲存2至6的所有數字,再創建answer變數表示變數x中相對應的元素是否為質數,而answer皆預設為True
```python=
x = list(range(2, 7))
answer = [True] * len(x)
```
* 讓我們一步一步來思考,可以先從3開始。
```python=
# 對3進行檢測(x物件中的第2個數字)
if x[1] % x[0] == 0: answer[1] = False
# 對4進行檢測(x物件中的第3個數字)
if x[2] % x[0] == 0: answer[2] = False
if x[2] % x[1] == 0: answer[2] = False
# 對5進行檢測(x物件中的第4個數字)
if x[3] % x[0] == 0: answer[3] = False
if x[3] % x[1] == 0: answer[3] = False
if x[3] % x[2] == 0: answer[3] = False
# 對6進行檢測(x物件中的第5個數字)
if x[4] % x[0] == 0: answer[4] = False
if x[4] % x[1] == 0: answer[4] = False
if x[4] % x[2] == 0: answer[4] = False
if x[4] % x[3] == 0: answer[4] = False
```
* 這樣我們可以透過answer來索引x,得到6以下的質數:
```python=
for idx in range(len(x)):
if answer[idx]:
print(x[idx])
```
## 第三節:巢狀迴圈(3)
* 聰明的你是不是已經發現某些規律,我們可以先利用變數取代部分索引的數字。
```python=
# 宣告變數
x = list(range(2, 7))
answer = [True] * len(x)
i = 1
# 對3進行檢測(x物件中的第2個數字)
if x[i] % x[0] == 0: answer[i] = False
i = 2
# 對4進行檢測(x物件中的第3個數字)
if x[i] % x[0] == 0: answer[i] = False
if x[i] % x[1] == 0: answer[i] = False
i = 3
# 對5進行檢測(x物件中的第4個數字)
if x[i] % x[0] == 0: answer[i] = False
if x[i] % x[1] == 0: answer[i] = False
if x[i] % x[2] == 0: answer[i] = False
i = 4
# 對6進行檢測(x物件中的第5個數字)
if x[i] % x[0] == 0: answer[i] = False
if x[i] % x[1] == 0: answer[i] = False
if x[i] % x[2] == 0: answer[i] = False
if x[i] % x[3] == 0: answer[i] = False
```
* 可以發現對於每個數字,檢查是否整除的程式碼行數不同,而越後面的數字行數越多。
## 第三節:巢狀迴圈(4)
* 這時同學們是不是其實已經想到,我們可以再撰寫一個內圈(inner loop)。
* 我們可以先這樣理解,將內圈以變數j替代。
```python=
# 宣告變數
x = list(range(2, 7))
answer = [True] * len(x)
# 外圈:對3進行檢測(x物件中的第2個數字)
i = 1
# 內圈
j = 0
if x[i] % x[j] == 0: answer[i] = False
# 外圈:對4進行檢測(x物件中的第3個數字)
i = 2
# 內圈
j = 0
if x[i] % x[j] == 0: answer[i] = False
j = 1
if x[i] % x[j] == 0: answer[i] = False
# 外圈:對5進行檢測(x物件中的第4個數字)
i = 3
# 內圈
j = 0
if x[i] % x[j] == 0: answer[i] = False
j = 1
if x[i] % x[j] == 0: answer[i] = False
j = 2
if x[i] % x[j] == 0: answer[i] = False
# 外圈:對6進行檢測(x物件中的第5個數字)
i = 4
# 內圈
j = 0
if x[i] % x[j] == 0: answer[i] = False
j = 1
if x[i] % x[j] == 0: answer[i] = False
j = 2
if x[i] % x[j] == 0: answer[i] = False
j = 3
if x[i] % x[j] == 0: answer[i] = False
```
* 讓我們先關注於6的部分,似乎可以將這些程式碼合成一個迴圈。
```python=
# 外圈:對6進行檢測(x物件中的第5個數字)
i = 4
# 內圈
for j in range(4):
if x[i] % x[j] == 0: answer[i] = False
```
* 找到內圈的規律後,我們將其推廣到外圈(outer loop),並且對於每個j剛好到i-1。
```python=
# 宣告變數
x = list(range(2, 7))
answer = [True] * len(x)
# 外圈:對3進行檢測(x物件中的第2個數字)
i = 1
# 內圈
for j in range(i):
if x[i] % x[j] == 0: answer[i] = False
# 外圈:對4進行檢測(x物件中的第3個數字)
i = 2
# 內圈
for j in range(i):
if x[i] % x[j] == 0: answer[i] = False
# 外圈:對5進行檢測(x物件中的第4個數字)
i = 3
# 內圈
for j in range(i):
if x[i] % x[j] == 0: answer[i] = False
# 外圈:對6進行檢測(x物件中的第5個數字)
i = 4
# 內圈
for j in range(i):
if x[i] % x[j] == 0: answer[i] = False
```
## 練習4:巢狀迴圈與條件判斷的組合
* 同學們應該已經有能力完成接下來的步驟,透過巢狀迴圈列出2到6內所有的「質數」,並有能力推廣到更多的數目。
* 問題1:請試著理解剛剛的每一個過程,並撰寫巢狀迴圈找出100以下的質數有哪些。
<details>
<summary>解答</summary>
- 同學們其實可以發現range(0)是不會進入內圈的,所以下列的程式碼會看起來更簡潔。
```python=
num_to_slove = 101
# 宣告變數
x = list(range(2, num_to_slove))
answer = [True] * len(x)
for i in range(len(x)):
for j in range(i):
if x[i] % x[j] == 0:
answer[i] = False
for idx in range(len(x)):
if answer[idx]:
print(x[idx])
```
</details>
* 問題2:事實上質數可以再更精確的定義為找不到比自己更小的可整除的「質數」,如5以下的「質數」為2、3,因此在檢查5的時候我們其實不需要檢查4這個數字。
<details>
<summary>解答</summary>
- 除此之外,「break」語句能夠提早終止迴圈。
```python=
num_to_slove = 101
# 宣告變數
x = list(range(2, num_to_slove))
answer = [True] * len(x)
for i in range(len(x)):
for j in range(i):
if answer[j]:
if x[i] % x[j] == 0:
answer[i] = False
break
for idx in range(len(x)):
if answer[idx]:
print(x[idx])
```
</details>
## 總結
* 同學們請回去務必練習並熟悉這些相關的程式語法。
* 資料型態轉換。
* 條件判斷式。
* 迴圈。
* 這堂課主要在教導如何撰寫條件判斷式與迴圈,同學們應該已有能力用Python處理許多任務。
* 熟悉迴圈指令將能夠讓電腦幫你處理許多常規的重複工作!