<style> .flex-row { display: flex; justify-content: space-evenly; align-items: center; } </style> #### SPROUT Python 2025, Week 5 # Function #### 講師:蔡政廷 --- ## 什麼是函式 ---- 在數學中 函數(function) 是一個有輸入跟輸出的黑盒子 <div class="flex-row"> ![2025-03-22-18-00-56](https://hackmd.io/_uploads/r1e_q-2hJe.png) \begin{align} f(x)&\overset{\mathrm{def}}{=}2x+3 \\ f(3)&=9 \\ f(7)&=17 \\ &\vdots \end{align} </div> ---- 在程式中 函式(function) 也可以是數學中的函數… ```python def f(x): return 2*x + 3 print(f(3)) # 9 print(f(7)) # 17 ``` ---- …但也可以僅僅是一段程式碼 ```python def greet(name): s = 'Hello, ' + name + '!' print(s) greet('Alice') # Hello, Alice! greet('Bob') # Hello, Bob! ``` 因此,程式中的函式 是更為籠統的「子程式」(subroutine) --- ## Python 函式基本語法 ---- ### 基本語法 ```python # 定義(define)函式 def 函式名稱(參數1, 參數2, ...): # 參數可以有 0 到很多個 # 你要做的事情 # ... return 回傳值 # 不一定要有 # 使用╱呼叫(call)函式 函式名稱(引數1, 引數2, ...) ``` ---- ### 範例(再看一次) ```python def f(x): return 2*x + 3 print(f(3)) # 9 print(f(7)) # 17 ``` * `f`:函式名稱 * `x`:參數(parameter) * `2*x + 3`:回傳值 * `3`、`7`:引數(argument) --- ## 作用域 ---- ### 作用域 ```python def greet(name): s = 'Hello, ' + name + '!' print(s) greet('Alice') # Hello, Alice! greet('Bob') # Hello, Bob! print(s) # NameError: name 's' is not defined ``` 在函式內定義的變數 只作用於函式範圍內 ---- ### 更改參數的值 ```python def set_to_42(x): x = 42 a = 7 set_to_42(a) print(a) # 7 還是 42? ``` ---- ### (並沒有)更改參數的值 ```python def set_to_42(x): # 把 x 指到傳進來的參數值 x = 42 # 把 x 指到 42 a = 7 set_to_42(a) print(a) # 7 ``` 在 Python 當中 有「賦值」(assign) 與「更改」(modify) 這兩種操作 「賦值」可以讓變數指向不同的值 「更改」可以更改變數指向的值 `int`、`string` 等不能被更改(immutable) ---- ### 賦值與更改 Python 的 `list` 可以被更改(mutable) ```python def assign(x): x = [2025, 3, 23] def modify1(x): x[0] = 42 def modify2(x): x.append(42) a = [1, 2, 3] assign(a) print(a) # [1, 2, 3] modify1(a) print(a) # [42, 2, 3] modify2(a) print(a) # [42, 2, 3, 42] ``` --- ## 實用語法 ---- ### 指定參數&回傳值型別 可以在定義函式時指定型別 但 Python 不會真的去檢查 所以比較像註解 ```python def f(x: int) -> int: return 2*x + 3 print(f(3)) # 9 print(f(7)) # 17 print(f('Hello')) # 錯誤,但不是因為檢查了型別 ``` ---- ### 回傳多個值 `return` 可以一次回傳多個值 用逗號隔開就好 呼叫時用一樣的順序拿值 ```python def min_max(n): x = min(n) y = max(n) return x, y n = [8, 4, 3, 7, 1] a, b = min_max(n) print(a) # 1 print(b) # 8 ``` ---- ### 參數預設值 可以設定參數的預設值 呼叫時沒指定引數就用預設值 ```python def greet(name, time='morning'): print(f'Good {time}, {name}!') greet('John') # Good morning, John! greet('John', 'evening') # Good evening, John! greet('John', 'night') # Good night, John! ``` ---- ### 潛在問題? 如果前面的參數想用預設值 但後面的參數想自己指定 Python 根本看不出來啊! ```python def greet(name, time='morning', mood='Good'): print(f'{mood} {time}, {name}!') greet('John') # Good morning, John! # 想要:Bad morning, John! greet('John', 'Bad') # Good Bad, John! # 可以但很爛的解法,需要把前面的預設值抄下來 greet('John', 'morning', 'Bad') # Bad morning, John! ``` ---- ### 表明參數! 在呼叫函式的時候 直接指定「哪個參數=哪個引數」就好了 ```python def greet(name, time='morning', mood='Good'): print(f'{mood} {time}, {name}!') greet('John', mood='Bad') # Bad morning, John! ``` ---- ### 不定數目參數 在參數前面加個星號 呼叫時就可以把後面所有參數 包成一個 iterable ```python def times_n(n, *x): for i in x: print(i*n) times_n(2, 3) # 6 times_n(2, 1, 2, 3) # 4 # 6 # 8 ``` ---- ### 上課練習 [666. 哪裡買TELSA電動車卡便宜](https://tioj.sprout.tw/contests/16/problems/666) [711. 比24還要好笑的笑話](https://tioj.sprout.tw/contests/16/problems/711) --- ## 遞迴 ## Recursion ---- ### 什麼是遞迴 函式可以呼叫自己 這個動作稱為遞迴 ```python def f(): f() f() # RecursionError: maximum recursion depth exceeded ``` (蛤,這不是出錯了嗎) ---- ### 中止條件 遞迴函式必須要有「中止條件」 比方說階乘: $$ n! = \begin{cases} 1 & \textrm{if } n = 0 \\ n \times (n-1)! & \textrm{otherwise} \\ \end{cases} \quad \forall n \in \mathbb N $$ $0! = 1$ 就是中止條件 因為這個情況不用算其他的階乘 ---- ### 用 Python 實作階乘 ```python def f(n): if n == 0: return 1 return n * f(n - 1) print(f(1)) # 1 print(f(2)) # 2 print(f(3)) # 6 print(f(4)) # 24 print(f(5)) # 120 ``` ---- ### 遞迴的優勢 同樣的功能,用迭代也能做: ```python def f(n): product = 1 for i in range(1, n+1): product *= i; return product; print(f(5)) # 120 ``` 但遞迴的優勢在於「絕佳的可讀性」 ---- ### 費氏數列 $$ \begin{gather} f(x) = \begin{cases} 1 & \textrm{if } x=0 \textrm{ or } x=1 \\ f(x-1) + f(x-2) & \textrm{otherwise} \end{cases} \\ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, \dots \end{gather} $$ ---- ### Python 實作 <div class="flex-row"> <div> 迭代法: ```python def f(x): a = 1 b = 1 for i in range(x-1): t = a a = b b = t + b return b print(f(7)) # 21 ``` </div> <div> 遞迴法: ```python def f(x): if x == 0 or x == 1: return 1 return f(x-1) + f(x-2) print(f(7)) # 21 ``` </div> </div> 使用遞迴時 你會發現自己根本只要照抄問題定義 --- ## 頭等公民 ---- 在 Python 當中,函式是「頭等公民」 意思是函式可以: * 存到變數裡 * 作為參數 * 作為回傳值 * 在函式裡定義 ---- ### 把函式存到變數裡 ```python def f(x): return 2*x + 3 print(f(7)) # 17 g = f print(g(7)) # 17 ``` ---- ### 把函式當參數 範例:把運算「向量化」 ```python def vector_op(x: list, y: list, f) -> list: z = [] for i in range(len(x)): z.append(f(x[i], y[i])) return z def add(x, y): return x + y def mul(x, y): return x * y a = [1, 2, 3] b = [4, 5, 6] print(vector_op(a, b, add)) # [5, 7, 9] print(vector_op(a, b, mul)) # [4, 10, 18] ``` ---- ### 在函式裡定義函式&作為回傳值 範例:發放消費券 ```python def create_coupon(multiplier): def coupon(money): return money * multiplier return coupon times3 = create_coupon(3) times5 = create_coupon(5) print(times3(1000)) # 3000 print(times5(1000)) # 5000 ``` ---- ### Lambda 函式 可以用 `lambda` 來定義一些很短的函式 ```python # lambda 參數1, 參數2, ...: 回傳值 print((lambda x: 2*x + 3)(7)) # 17 ``` Lambda 函式沒有名字 但你可以把它存到變數裡 然後用那個變數的名字 ```python f = lambda x: 2*x + 3 print(f(7)) # 17 ``` ---- ### 把運算「向量化」,但是 lambda ```python def vector_op(x: list, y: list, f) -> list: z = [] for i in range(len(x)): z.append(f(x[i], y[i])) return z a = [1, 2, 3] b = [4, 5, 6] print(vector_op(a, b, lambda x, y: x+y)) # [5, 7, 9] print(vector_op(a, b, lambda x, y: x*y)) # [4, 10, 18] ``` ---- ### 發放消費券,但是 lambda ```python create_coupon = lambda multiplier: lambda money: money * multiplier times3 = create_coupon(3) times5 = create_coupon(5) print(times3(1000)) # 3000 print(times5(1000)) # 5000 ``` --- # 下課 大作業加油~ ### 其他習題 [761. 超級貓貓星際漫遊-3](https://tioj.sprout.tw/contests/16/problems/761)
{"slideOptions":"{\"transition\":\"slide\"}","title":"Function","contributors":"[{\"id\":\"6f837504-512a-4e38-b145-0e8a28bc74ab\",\"add\":8257,\"del\":1761}]","description":"在數學中函數(function)是一個有輸入跟輸出的黑盒子"}
    118 views