C++ 小社
函式與遞迴
【簡報】蘇西
【講師】children
什麼是函式
抽象化 (abstraction):
在電腦科學中,抽象化(英語:Abstraction)是將資料與程式,以它的語意來呈現出它的外觀,但是隱藏起它的實作細節。抽象化是用來減少程式的複雜度
簡言之,如果要重複處理相同的運算,使用一個函式把那組運算包起來,可以
1) 減少寫重複的程式碼 2) 方便除錯
什麼是函式
數學上的函式:
輸入一個值
對輸入的值進行什麼運算
什麼是函式
程式上的函式:
int function(int x){
return x*x + x + 2;
}int main (){
int x;
cin >> x;
cout << function(x);
}創造這個函式
呼叫這個函式
註:return 就是回傳一個結果的意思
怎麼寫函式
int function (int x){
return x*x + x + 2;
}回傳值的變數型態
函式的名稱
輸入的值
- 若函示沒有要回傳任何值,則「回傳的變數型態」可以寫 void
- 函式的名稱可以自己隨意取
運算
怎麼叫函式
function(3);函式的名稱
輸入的值
定義 vs 宣告
int func(int a); // 宣告
int func(int a){ // 進行定義
return a;
}- 宣告結尾要加分號,而且只有一行。
- 定義時加上兩個中括號,裡面就開始寫內容了。
其實直接定義就好,不一定要宣告。但宣告可以增加專案時程式碼的易讀性。
預設函數值
int sum(int a, int b=20) // 預設 b 為 20
{
int result;
result = a + b;
return (result);
}
我們可以為函數當中的參數設定預設值,如果我們沒有輸入值進去的話,則預設使用這個數字。
呼叫函式值
// 函數宣告
void swap(int x, int y);
// 函數定義
void swap(int x, int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 指定给 x */
y = temp; /* 把 x 指定给 y */
return;
}假設我們今天寫一個函式來交換兩個數值:
(把 x 變 y,把 y 變 x)
呼叫函式值
看起來很正確,那我們呼叫看看
int main ()
{
int a = 100;
int b = 200;
cout << "交換前,a 的值:" << a << endl;
cout << "交換前,b 的值:" << b << endl;
// 呼叫函數交換值
swap(a, b);
cout << "交換後,a 的值:" << a << endl;
cout << "交換後,b 的值:" << b << endl;
return 0;
}呼叫函式值
跑出來超怪
交換前,a 的值:100
交換前,b 的值:200
交換後,a 的值:100
交換後,b 的值:200為什麼呢?
呼叫函式值
呼叫函式其實有三種方式:
1. 傳值呼叫 (call by value)
2. 傳址呼叫 (call by address / call by pointer)
3. 傳參考呼叫 (call by reference)
目前為止我們用的是第一種 call by value,
函式收到的事實上只有 變數的「副本」(非變數本體)
(優點:不會不小心改變原本變數)
(缺點:不會改變原本變數(?)
所以我們要學另外兩種
呼叫函式值 - 傳址呼叫
仙貝芝士🍘🧀 :指摽 (pointer) 與 參考值 (reference)
int a = 10;
int *p = &a; p 是指向 int a 的指標,存放 a 在電腦記憶體裡的位址
& a 可以「取 a 的記憶體位址」
*p 可以得到 a 的值
呼叫函式值 - 傳址呼叫
也就是說
int a = 10;
int *p = &a;
cout << &a; // 印出 a 的地址 (=一串亂碼)
cout << p; // 一樣印出 a 的地址 (=一串亂碼)
cout << *p; // 印出在 p 地址的東西的數值(= a的數值 = 10)
呼叫函式值 - 傳址呼叫
#include <iostream>
using namespace std;
// 函數宣告
void swap(int *x, int *y);
// 函數定義
void swap(int *x, int *y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 指定给 x */
*y = temp; /* 把 x 指定给 y */
return;
}
利用指標直接從記憶體抓輸入值,就不會抓到副本,可以抓到本體囉
呼叫函式值 - 傳址呼叫
int main ()
{
int a = 100;
int b = 200;
swap(&a, &b);
return 0;
}記得呼叫函式時,要用 & 取址
這樣就可以成功交換 a 和 b 了!
呼叫函式值 - 傳參考值呼叫
另外,還有另一種方法可以達到一樣的效果
就是「傳參考值呼叫」(call by reference)
void swap(int &x, int &y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 指定给 x */
y = temp; /* 把 x 指定给 y */
return;
}int main ()
{
int a = 100;
int b = 200;
swap(a, b);
return 0;
}注意我們 & 只寫了一遍,比較方便。
* 是在 C 就有的東西,& 是在 C++ 才發明出來的方便好東西
遞迴 Recursion
函式最常見的應用就是遞迴了!
遞迴就是一個函式(function)在執行的過程中又呼叫自己本身。
例如:費波那契數列 (每項為前兩項的和)
這就是一種自己呼叫自己
遞迴 Recursion
構成遞迴需要兩個要件:
1. Base case (基本情況 = 何時停止遞迴)
2. Recursive Case (遞迴情況)
int fibonacci(int n) {
if (n == 0) // base case 1
return 0;
else if (n == 1) // base case 2
return 1;
else // recursive case
return fibonacci(n - 1) + fibonacci(n - 2);
}
(註:我們在前面多補一項,把 n=0 時的值設為0)
費波那契用程式實作:

base case 存在讓函式知道什麼時候不用在繼續「往下呼叫自己」
遞迴 Recursion
從遞迴可以看出函數的實用性了吧!
你們可以實作看看階乘的遞迴嗎
階乘的定義:
舉例來說:
遞迴 Recursion
無聊的話可以寫寫看帕斯卡三角形的遞迴

怎麼寫程式輸出這坨東西
解答在下一頁
遞迴 Recursion
進階挑戰:把他依照三角形的格式對齊
沒解答自己想
int pascal(int n, int k) {
// Base cases
if (k == 0 || k == n)
return 1;
// Recursive case
return pascal(n - 1, k - 1) + pascal(n - 1, k);
}
下課囉
記得寫作業
記得報寒訓
寒訓連結在 DC
C++ 小社 - 函式與遞迴
By Suzy Huang
C++ 小社 - 函式與遞迴
- 121