Python 的內建函式簡化了許多常見操作,例如 enumerate() 能同時取得序列的索引和值,而 len() 則能快速取得序列長度。除了內建函式,Python 也允許自定義函式,並可設定預設引數值,提升程式碼的彈性。理解變數範圍對於程式碼的正確執行至關重要,區域變數僅限函式內部使用,而全域變數則可在整個程式碼中存取。控制流程的 continuebreak 陳述式能有效控制迴圈的執行,而 try-except 區塊則提供錯誤處理機制,增強程式的穩定性。模組的匯入擴充套件了 Python 的功能,例如 math 模組提供了數學運算函式。生成器則是一種特殊的迭代器,能有效處理大量資料,避免一次性載入所有資料到記憶體,從而提升程式效率。遞迴是一種重要的程式設計技巧,函式呼叫自身,直到滿足基本條件,適用於解決可分解成相似子問題的複雜問題,例如計算階乘和費氏數列。Josephus 問題則是一個經典的數學問題,可透過 Python 程式碼模擬其淘汰過程。

Python 基礎函式與變數範圍

在 Python 中,內建函式如 enumerate()len() 被廣泛使用。enumerate() 函式可以在迭代序列時同時取得索引和對應的值。

使用 enumerate() 函式

enumerate() 函式可以將序列轉換為可列舉的物件。以下範例展示如何使用 enumerate()

l1 = ['x', 1, 9, 'cs']
enum = dict(enumerate(l1, 4))
print(enum)
# 輸出:{4: 'x', 5: 1, 6: 9, 7: 'cs'}

t1 = ('x', 1, 9, 'cs')
enum = list(enumerate(t1))
print(enum)
# 輸出:[(0, 'x'), (1, 1), (2, 9), (3, 'cs')]

內容解密:

  1. enumerate(l1, 4) 將列表 l1 轉換為可列舉的物件,並從索引 4 開始。
  2. dict(enumerate(l1, 4)) 將可列舉的物件轉換為字典。
  3. list(enumerate(t1)) 將元組 t1 轉換為可列舉的物件,並將其轉換為列表。

使用 len() 函式

len() 函式用於取得序列的長度。以下範例展示如何使用 len()

r = [1, 4, 9, 8, 5]
t = len(r)
print(t)
# 輸出:5

s = {1, 4, 7, 2}
print(len(s))
# 輸出:4

st = 'ComputerScience'
print(len(st))
# 輸出:15

內容解密:

  1. len(r) 取得列表 r 的長度。
  2. len(s) 取得集合 s 的長度。
  3. len(st) 取得字串 st 的長度。

在迴圈中使用 enumerate()

在迴圈中使用 enumerate() 可以同時取得索引和對應的值。以下範例展示如何在迴圈中使用 enumerate()

s = 'cs ds'
for j in enumerate(s):
    print(j)
# 輸出:
# (0, 'c')
# (1, 's')
# (2, ' ')
# (3, 'd')
# (4, 's')

s = 'cs'
for j in enumerate(s, 2):
    print(j)
# 輸出:
# (2, 'c')
# (3, 's')

內容解密:

  1. enumerate(s) 將字串 s 轉換為可列舉的物件。
  2. enumerate(s, 2) 將字串 s 轉換為可列舉的物件,並從索引 2 開始。

自定義函式

Python 使用者可以自定義函式。自定義函式的語法如下:

def FuncName(parameter1, parameter2, ..., parametern):
    statement1
    statement2
    ...
    statementn
    return result

以下範例展示如何定義和使用自定義函式:

def SimpleCalc(operator='+', operand1=20, operand2=16):
    if operator == '+':
        res = operand1 + operand2
    elif operator == '*':
        res = operand1 * operand2
    elif operator == '-':
        res = operand1 - operand2
    elif operator == '/':
        res = operand1 / operand2
    return res

t = SimpleCalc()
print(t)
# 輸出:36

t = SimpleCalc('*', 12, 2)
print(t)
# 輸出:24

內容解密:

  1. SimpleCalc() 定義了一個自定義函式,預設引數為 operator='+'operand1=20operand2=16
  2. 當沒有傳遞引數時,使用預設引數進行計算。
  3. 當傳遞引數時,使用傳遞的引數進行計算。

變數範圍

變數的範圍可以是區域或全域。區域變數只能在函式內使用,而全域變數可以在整個程式中使用。

def add(a, v):
    c = a + v
    r = 4
    r = r * v
    return c

d = add(11, 9)
print(r)
# 輸出:NameError: name 'r' is not defined

內容解密:

  1. r 是區域變數,只能在函式 add() 內使用。
  2. 在函式外嘗試存取 r 時,會引發 NameError

使用 global 關鍵字可以將區域變數宣告為全域變數。

def add(a, v):
    c = a + v
    global r
    r = 4
    r = r * v
    return c

d = add(11, 9)
print(r)
# 輸出:36

內容解密:

  1. 使用 global rr 宣告為全域變數。
  2. 在函式外可以存取全域變數 r

Lambda 函式

Lambda 函式是一種簡潔的函式定義方式,通常用於需要小型、簡單函式的場合。

sum = lambda x, y: x + y
print(sum(3, 4))
# 輸出:7

內容解密:

  1. lambda x, y: x + y 定義了一個 lambda 函式,接受兩個引數並傳回它們的和。
  2. 將 lambda 函式指定給變數 sum,並呼叫它來計算結果。

2.13.3 控制流程處理

在某些情況下,需要處理不想要的情況,例如不需要的物件或錯誤。在這種情況下,可以考慮不同的處理方式,例如離開迴圈或忽略錯誤。

2.13.3.1 Continue

在下面的範例中,使用 continue 關鍵字,如果一個數字是偶數,則會被忽略,迴圈會繼續直到結束。

lst = [5, 6, 2, 3, 0, 13, 4]
lst0 = []
for i in lst:
    if i % 2 == 0:
        continue
    lst0.append(i)
print(lst0)
# 輸出:[5, 3, 13]

內容解密:

  1. lst 是一個包含數字的列表。
  2. for 迴圈遍歷 lst 中的每個元素。
  3. 如果元素 i 是偶數(即 i % 2 == 0),則 continue 關鍵字會跳過當前迴圈迭代,直接進入下一次迭代。
  4. 如果元素 i 不是偶數,則將其追加到 lst0 列表中。
  5. 最後,列印出 lst0,其中包含所有從 lst 中篩選出的奇數。

2.13.3.2 Break

在下面的範例中,使用 break 關鍵字,如果找到一個偶數,則會終止迴圈。

lst = [5, 6, 2, 3, 0, 13, 4]
lst0 = []
for i in lst:
    if i % 2 == 0:
        break
    lst0.append(i)
print(lst0)
# 輸出:[5]

內容解密:

  1. lst 是一個包含數字的列表。
  2. for 迴圈遍歷 lst 中的每個元素。
  3. 如果元素 i 是偶數(即 i % 2 == 0),則 break 關鍵字會立即終止迴圈。
  4. 如果元素 i 不是偶數,則將其追加到 lst0 列表中。
  5. 由於在遇到第一個偶數 6 時迴圈被終止,因此 lst0 只包含了 [5]

2.13.3.3 Try Except

當執行程式時,可能會發生錯誤,從而阻止程式執行。Python 中可以使用 tryexcept 結構來處理錯誤。

a = 10
b = 21
try:
    c = a + b + 'b'
    print(c)
except ValueError:
    print('Value Error')
except IndexError:
    pass
except ZeroDivisionError:
    print('zero error')
except TypeError:
    print(a, b)

內容解密:

  1. try 區塊中,嘗試執行可能引發錯誤的程式碼。
  2. 在這個例子中,嘗試將整數 ab 與字串 'b' 相加,這會引發 TypeError
  3. 當發生錯誤時,Python 會尋找相應的 except 區塊來處理該錯誤。
  4. 在這個例子中,由於發生了 TypeError,因此執行對應的 except TypeError 區塊,列印出 ab 的值。

2.14 模組

模組是包含預定義函式的程式碼集合,可以透過匯入使用。要匯入模組,可以使用 import 關鍵字,後面跟著模組的名稱。

使用 math 模組

import math
t = math.cos(2.8)
print(t)
# 輸出:-0.9422223406686581
t0 = math.log2(43)
print(t0)
# 輸出:5.426264754702098

內容解密:

  1. 使用 import mathmath 模組匯入。
  2. 使用 math.cos(2.8) 計算餘弦值,並將結果儲存在變數 t 中。
  3. 使用 math.log2(43) 計算以 2 為底的對數值,並將結果儲存在變數 t0 中。

2.15 生成器

生成器是傳回迭代器的函式,而不是生成一個值。它們在處理大量資料時非常有用,因為它們允許只將必要的資料部分載入記憶體,而不是載入整個資料集。

遞迴與產生器在 Python 中的應用

在探討遞迴與產生器之前,我們先來瞭解 Python 中的一些基本概念。Python 是一種功能強大的程式語言,它支援多種程式設計正規化,包括函式式程式設計。在本章中,我們將重點介紹遞迴和產生器這兩個重要的概念。

2.16 遞迴

遞迴是一種程式設計技術,其中一個函式會呼叫自己,直到達到某個基本條件(base case)。這種技術對於解決那些可以分解為較小、相似子問題的複雜問題非常有效。下面是一個使用遞迴計算階乘的例子:

def factorial(n):
    # 檢查 n 是否等於 0(基本條件)
    if n == 0:
        # 傳回 1 作為 0 的階乘
        return 1
    else:
        # 遞迴計算 n 的階乘
        return n * factorial(n-1)

內容解密:

  1. if n == 0: 這行程式碼檢查是否達到基本條件(n 等於 0)。如果是,則傳回 1,因為 0 的階乘定義為 1。
  2. return n * factorial(n-1) 這行程式碼是遞迴呼叫。如果 n 不是 0,則函式呼叫自己,傳入 n-1,並將結果乘以 n。

同樣地,下面是一個使用遞迴計算第 n 個 Fibonacci 數的例子:

def fibonacci(n):
    # 檢查 n 是否小於或等於 1(基本條件)
    if n <= 1:
        # 傳回 n 作為 Fibonacci 數
        return n
    else:
        # 遞迴計算 Fibonacci 數
        return fibonacci(n-1) + fibonacci(n-2)

內容解密:

  1. if n <= 1: 這行程式碼檢查是否達到基本條件(n 小於或等於 1)。如果是,則傳回 n,因為第 0 和第 1 個 Fibonacci 數分別是 0 和 1。
  2. return fibonacci(n-1) + fibonacci(n-2) 這行程式碼是遞迴呼叫。如果 n 大於 1,則函式呼叫自己兩次,分別傳入 n-1n-2,並將結果相加。

Josephus 問題

Josephus 問題是一個理論上的電腦科學問題。這個問題涉及一群人圍成一個圓圈,等待處決。每次處決一個人後,會跳過一定數量的人,然後再處決下一個人。這個過程繼續直到只剩下一個人。我們的目標是開發一個函式,給定人數 n 和跳過人數 k,傳回處決的順序。

演算法步驟

  1. 建立一個列表 m,包含從 1 到 n 的數字,代表圓圈中的人。
  2. 建立一個空列表 ans,用於儲存輸出結果。
  3. 設定變數 i 為 0。
  4. 使用 while 迴圈,直到列表 m 為空。
  5. 在 while 迴圈中,利用公式 (i + k - 1) mod len(m) 更新 i,確保 i 在列表 m 的範圍內。
  6. 使用 pop 方法從列表 m 中移除索引為 i 的元素,並將其追加到列表 ans 中。
  7. 傳回列表 ans 作為函式的輸出。

下面是 Josephus 問題的 Python 程式碼:

def josephus(n, k):
    m = list(range(1, n+1))
    ans = []
    i = 0
    while m:
        i = (i + k - 1) % len(m)
        ans.append(m.pop(i))
    return ans

內容解密:

  1. m = list(range(1, n+1)) 這行程式碼建立一個列表,包含從 1 到 n 的數字。
  2. i = (i + k - 1) % len(m) 這行程式碼更新 i 的值,確保它在列表 m 的範圍內。
  3. ans.append(m.pop(i)) 這行程式碼移除列表 m 中索引為 i 的元素,並將其追加到列表 ans 中。

數學問題解析與實作

在本文中,我們將探討三個數學問題:Josephus 問題、格子路徑問題和布魯塞爾選擇問題。這些問題都具有有趣的數學背景和實際應用價值。

Josephus 問題

Josephus 問題是一個經典的數學問題,描述了一群人圍成一個圈,按照一定的規則淘汰,直到只剩下最後一個人。這個問題可以用來模擬各種現實生活中的淘汰機制。

Josephus 問題的 Python 實作

def josephus(n, k):
    '''
    Josephus 問題的 Python 實作
    '''
    m = list(range(1, n + 1))  # 建立一個包含 1 到 n 的數字的列表
    ans = []  # 建立一個空列表來儲存被淘汰的人的順序
    i = 0  # 初始化索引變數
    
    while m:
        # 計算下一個被淘汰的人的索引
        i = (i + k - 1) % len(m)
        # 將被淘汰的人從列表中移除並加入到 ans 列表中
        ans.append(m.pop(i))
    
    return ans

Josephus 函式運作解析

Josephus 函式首先建立一個包含 1 到 n 的數字的列表 m,然後初始化一個空列表 ans 來儲存被淘汰的人的順序。變數 i 用來記錄當前索引位置。在 while 迴圈中,函式計算下一個被淘汰的人的索引,將其從 m 列表中移除並加入到 ans 列表中,直到 m 列表為空。最後,函式傳回 ans 列表,包含了被淘汰的人的順序。

格子路徑問題

格子路徑問題是一個典型的動態規劃問題,描述了一個人在格子網格中從某個點出發,只能向左或向下移動,求到達另一個點的路徑數量。

格子路徑問題的 Python 實作

def lattice_paths(row, col, tabu):
    '''
    格子路徑問題的 Python 實作
    '''
    paths = [[0] * (col + 1) for _ in range(row + 1)]  # 建立一個二維陣列
    paths[0][0] = 1  # 初始化左上角的值為 1
    
    for i in range(1, row + 1):
        if (i, 0) not in tabu:
            paths[i][0] = paths[i - 1][0]
    
    for j in range(1, col + 1):
        if (0, j) not in tabu:
            paths[0][j] = paths[0][j - 1]
    
    for i in range(1, row + 1):
        for j in range(1, col + 1):
            if (i, j) not in tabu:
                paths[i][j] = paths[i - 1][j] + paths[i][j - 1]
    
    return paths[row][col]

格子路徑函式運作解析

格子路徑函式首先建立一個二維陣列 paths,然後初始化左上角的值為 1。接著,函式填充第一列和第一行的值,如果某個格子不在 tabu 列表中,則其值等於上方或左方格子的值。然後,函式填充剩餘的格子的值,每個格子的值等於上方和左方格子的值之和。最後,函式傳回右下角格子的值,代表了到達該點的路徑數量。

布魯塞爾選擇問題

布魯塞爾選擇問題是一個需要建立一個函式來生成滿足特定條件的數字列表的問題。

布魯塞爾選擇問題的解析

布魯塞爾選擇問題需要對輸入數字 n 的數字進行操作,根據子集的大小 k 對數字進行除以 2 或乘以 2 的操作,然後將結果排序後傳回。