> For the complete documentation index, see [llms.txt](https://simplife.gitbook.io/python/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://simplife.gitbook.io/python/xin-shou-cun/8.-han-shi-yu-di-hui.md).

# 8. 函式與遞迴

## I. 函式

### A. 何謂函式?

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

![](/files/kJehomBNnW59kAoka09C)

![](/files/iPdNnVkcdmDxEzvk8tgY)

### B. 為何需要函式?

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

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

&#x20;                  ![](/files/mknU9lsGgN4HRHh82vwB)                          ![](/files/6Rvo7rm3lX2NcxcXAjYD)

### C. 函式的基本結構 <a href="#python-zhong-de-han-shi-jie-gou" id="python-zhong-de-han-shi-jie-gou"></a>

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

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

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

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

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

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

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

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

### D. 函式呼叫 <a href="#han-shi-hu-jiao" id="han-shi-hu-jiao"></a>

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

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

<mark style="background-color:red;">3. 函式必須在呼叫前先定義完成</mark>

<mark style="background-color:red;">4. 參數可以有多個，且型態不拘</mark>

#### 1. 無參數的函式

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

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

#### 2. 單個參數的函式

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

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

#### 3. 多個參數的函式

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

> <mark style="background-color:purple;">主人用x與y參數傳給僕人，僕人方相對應的參數視為為a與b</mark>

#### 4. 未知個參數的函式

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

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

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

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

i = input('請輸入您想要的配料：').split()
make_snow_ice(i)
```

#### 5. 有預設值的參數

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

interest('吃飯') # 我的興趣是 吃飯 最喜歡的地方是 Taiwan
interest('吃飯', 'New Zealand') # 我的興趣是 吃飯 最喜歡的地方是 New Zealand
interest(area='New Zealand', type='吃飯') # 我的興趣是 吃飯 最喜歡的地方是 New Zealand
```

#### 6. 函式的相互呼叫

```python
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))
```

```python
def add(a, b):
    return a+b
    
def mul(a, b):
    return a*b

def running(func, arg1, arg2):
    return func(arg1, arg2)

print(running(add, 5, 10)) # 15
print(running(mul, 5, 10)) # 50
```

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

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

```python
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. 遞迴

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

![](/files/biBmxzs6lMvUlfL0o9Bn)

![](/files/RpsQnEOJR4UMez2X3zsv)

![](/files/31Nw5OtYDbyOaGskfcex)

![Recursion(遞迴)](/files/4JkfS9EzWQNqrSOuVaSA)

### A. 遞迴的必要條件

1. <mark style="background-color:red;">**遞迴關係式(需有回傳 ： 記憶)**</mark>
2. <mark style="background-color:red;">**有個最終停止的點(驚醒)**</mark>

### B. 範例

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

<mark style="background-color:green;">觀察</mark>：

1層香檳塔 → 1個杯子

2層香檳塔 → 5個杯子

&#x20;           ......

n層香檳塔 → ?

<mark style="background-color:green;">歸納</mark>：

是否存在遞迴關係式？Ａ：$$S\_n = S\_{n-1} + n × n$$

是否有最終停止的點？A：$$S\_1 = 1$$

<mark style="background-color:green;">程式碼</mark>：

```python
# 遞迴寫法(1)
def sum_champagne_glass(n):
    if n == 1:
        return 1
    else:
        return  sum_champagne_glass(n-1) + n*n
```

```python
# 遞迴寫法(2)
def sum_champagne_glass(n):
    if n == 1:
        return 1
    return  sum_champagne_glass(n-1) + n*n
```

```python
# 迴圈寫法
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階乘的值

<mark style="background-color:green;">觀察</mark>：

0! → 1

1! → 1

2! → 2

.....

n!  → ?

<mark style="background-color:green;">歸納</mark>：

是否存在遞迴關係式？Ａ：$$F\_{n} = F\_{n-1}  × n$$&#x20;

是否有最終停止的點？A：$$F\_{0} = 1$$

<mark style="background-color:green;">程式碼</mark>：

```python
# 遞迴寫法
def factorial(n):
    if n==0:
        return 1
    return factorial(n-1)*n
    
while 1:
    i = int(input('請輸入您想計算的階乘：'))
    print(factorial(i))
```

```python
# 迴圈寫法
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))
```
