8. 函式與遞迴

I. 函式

A. 何謂函式?

想必大家應該不陌生,在數學科目中應該多少有接觸到吧!!

B. 為何需要函式?

最主要其實是要將某個流程封裝、模組化,未來若需要進行同樣流程時,我們只需要呼叫這模組即可,程式碼能夠精簡化,也比較美觀

人偶爾會幻想如果有個僕人能幫我做牛做馬的那該有多好啊!!其實函式就像是個專業的僕人,你可以請他幫你完成一些事,不過要記住,他是個專業的僕人,不是萬能的僕人,專業的僕人只會做專業的幾件事喔!

C. 函式的基本結構

1. 無回傳值、無參數輸入型

def wash_dish():
    print('是!我立刻去洗盤子')

2. 無回傳值、有參數輸入型

def wash_dish(x):
    print('是!我立刻去洗',x,'個盤子')

3. 有回傳值、無參數輸入型

def wash_dish():
    s = "是!我立刻去洗盤子"
    return s

4. 有回傳值、有參數輸入型

def wash_dish(x):
    s = "是!我立刻去洗" + str(x) + "個盤子"
    return s

D. 函式呼叫

1. 既然函式可以視作僕人,呼叫函式的就是主人囉!

2. 在主人還沒呼叫函式前函式是不會有動作的

3. 函式必須在呼叫前先定義完成

4. 參數可以有多個,且型態不拘

1. 無參數的函式

# 函式必須要先定義,但不會做出動作
def wash_dish():
    s = "是!我立刻去洗盤子"
    return s

# 主人開始呼叫函式,這時函式才開始動作
print(wash_dish()) # 是!我立刻去洗盤子

2. 單個參數的函式

# 函式必須要先定義,但不會做出動作
def wash_dish(x):
    s = "是!我立刻去洗" + str(x) + "個盤子"
    return s

# 主人開始呼叫函式,這時函式才開始動作
print(wash_dish(5)) # 是!我立刻去洗5個盤子

3. 多個參數的函式

def add(a, b):
    return a + b
    
x, y = map(int, input("請輸入兩個整數:").split())
print(add(x, y))

主人用x與y參數傳給僕人,僕人方相對應的參數改為a與b。當然,仍沿用x與y名稱也可以

4. 未知個參數的函式

函式中參數前面加上*號,表示可輸入未知筆資料,所輸入的資料將存在陣列(tuple)中

def make_snow_ice(*toppings):
    print('您的雪花冰配料如下:')
    for topping in toppings:
        print(topping)

make_snow_ice('愛玉') # 您的雪花冰配料如下:愛玉
make_snow_ice('愛玉', '花生', '芋圓') # 您的雪花冰配料如下:愛玉 花生 芋圓

5. 各式型態的參數

# 有預設值的參數
def interest(type, area='Taiwan'):
    print('我的興趣是', type, '最喜歡的地方是', area)

interest('吃飯') # 我的興趣是 吃飯 最喜歡的地方是 Taiwan
interest('吃飯', 'New Zealand') # 我的興趣是 吃飯 最喜歡的地方是 New Zealand
interest(area='New Zealand', type='吃飯') # 我的興趣是 吃飯 最喜歡的地方是 New Zealand
# 陣列當參數
def find_max(input_list):
    return max(input_list)

num_list = [8, -7, 16, 5, -2]
print(find_max(num_list))
# 浮點數當參數
def BMI(h, w):
    return w/((h/100)**2)

height, weight = map(float, input('請輸入您的身高及體重:').split())
print(BMI(height, weight))

6. 函式的相互呼叫

def add(a, b):
    return a+b

def avg(a, b):
    return add(a, b)/2

while 1:
    m, n = map(int, input("請輸入兩個整數:").split())
    print(avg(m,n))

可以同時存在多個函式

7. 【補充】閉包(closure)函式

原先沒有closure,我們需要傳遞a, b, x參數,closure可以讓程式設計更有效率,同時未來擴充時,程式碼可以更容易移植

def outer(a, b):
    '''a和b是inner()的環境變數'''
    def inner(x):
        return a*x+b
    return inner
    
f1=outer(1, 2)
f2=outer(3, 4)
print(f1(1)) # 3
print(f2(3)) # 13

II. 遞迴

其實就是自己呼叫自己的函式

A. 遞迴的必要條件

  1. 遞迴關係式(需有回傳 : 記憶)

  2. 有個最終停止的點(驚醒)

B. 範例

【範例一】請用遞迴方式計算n層香檳塔共有幾個杯子

觀察

1層香檳塔 → 1個杯子

2層香檳塔 → 5個杯子

......

n層香檳塔 → ?

歸納

是否存在遞迴關係式?A:Sn=Sn1+n×nS_n = S_{n-1} + n × n

是否有最終停止的點?A:S1=1S_1 = 1

程式碼

# 遞迴寫法(1)
def sum_champagne_glass(n):
    if n == 1:
        return 1
    else:
        return  sum_champagne_glass(n-1) + n*n
# 遞迴寫法(2)
def sum_champagne_glass(n):
    if n == 1:
        return 1
    return  sum_champagne_glass(n-1) + n*n
# 迴圈寫法
def sum_champagne_glass(n):
    sum = 0
    for i in range(1, n+1):
        sum += i*i
    return sum

while 1:
    i = int(input("請輸入層數:"))
    print('總共有:',sum_champagne_glass(i),'個酒杯')

遞迴並不是在任何情況下都比迴圈來的好用

遞迴就只是自己呼叫自己的函式

【範例二】請用遞迴方式計算出n階乘的值

觀察

0! → 1

1! → 1

2! → 2

.....

n! → ?

歸納

是否存在遞迴關係式?A:Fn=Fn1×nF_{n} = F_{n-1} × n

是否有最終停止的點?A:F0=1F_{0} = 1

程式碼

# 遞迴寫法
def factorial(n):
    if n==0:
        return 1
    return factorial(n-1)*n
    
while 1:
    i = int(input('請輸入您想計算的階乘:'))
    print(factorial(i))
# 迴圈寫法
def factorial(n):
    accumulate = 1
    if n==0:
        return 1
    for i in range(2, n+1):
        accumulate *= i
    return accumulate

while 1:
    i = int(input('請輸入您想計算的階乘:'))
    print(factorial(i))

Last updated