每個 Matlab 函式,不論是內建的、外掛的還是使用者自己寫的, 都可以呼叫別的函式。例如在前一節的 collatz() 裡面, 呼叫了 nargin(), nargout(), disp(), length(), floor() 和 rem()。 其實 nargin() 和 nargout() 並不需要任何參數, 因此使用的時候不需要寫括號。 但是我還是寫 nargin() 和 nargout(), 那一對空括號只是一種書寫的習慣,表示 nargin 和 nargout 是函式而不是變數、也不是保留字。
Matlab 函式不但可以呼叫其他函式,它還可以呼叫自己,而且可以重複呼叫自己。 這種「重複執行一套給定的程序」的流程控制方式, 稱為 遞迴 (recursion)。
例如,若 n 是一個正整數,則 n! (n 階乘) 的迭代定義方式是
n! = 1 * 2 * 3 * ... * n用 Matlab 正統方法寫,應該是
prod(1:n)而如果硬要用迴圈,for 迴圈寫法是
而 while 迴圈的寫法是fac = 1; for k=2:n fac = fac*k; end
其實還有更聰明一點的寫法,但是這些都只是粗淺的例子, 沒必要耍把戲。fac = 1; k = 2; while (k <= n) fac = fac*k; k = k+1; end
其實階乘也有遞迴形式的定義,就是
定義 0! = 1,若 n>0 則定義 n! = n * (n-1)!如果要用電腦語言實現遞迴形式的定義,就要寫函式, 而且讓那個函式以遞迴的方式重複計算。
在 fac.m 檔案內寫一個 fac() 函式,如下。
function fn = fac(n) % Usage: fac(n) % % Here n is a nonnegative integer, and fn is n!. This function doesn't % validate the input argument. If n is not an integer, its floor is % assumed. If n is negative, 1 is returned. % % Shann 2004-05-05 n = floor(n); if (n > 1) fn = n * fac(n-1); else fn = 1; end
fac() 寫風格與 collatz() 有一處不同:
我們以 fac(3) 為例,看看 Matlab 做了什麼。
Matlab 呼叫 fac(3) | |||
fac(3) 執行 n = floor(3); fn = 3*fac(2); 故呼叫 fac(2) | |||
fac(2) 執行 n = floor(2); fn = 2*fac(1); 故呼叫 fac(1) | |||
fac(1) 執行 n = floor(1); fn = 1; | |||
fac(1) 結束,答案 (fn 是 1) 傳回 caller | |||
fac(2) 接到答案,繼續執行 fn = 2*1; | |||
fac(2) 結束,答案 (fn 是 2) 傳回 caller | |||
fac(3) 接到答案,繼續執行 fn = 3*2; | |||
fac(3) 結束,答案 (fn 是 6) 傳回 caller | |||
Matlab 接到答案,指派給 ans,顯示答案 6 |
以上 fac(3) 使得 fac() 一共被呼叫了「三層」。 Matlab 規定遞迴的上限是 100 層。
凡是可以用遞迴寫出來的程式,都可以用迭代技術寫成。 遞迴函式既不會執行得比較快,也不會節省記憶體空間。 事實上,遞迴函式比起寫得好的迭代程式, 既會執行得比較慢,也會花費更多記憶體空間。 那為什麼還要用遞迴呢? 因為有些程式用遞迴技術會好寫得多, 當電腦反正很快、記憶體很多的時候,不妨讓電腦忙一忙, 而讓寫程式的人輕鬆一點。
哪樣的程式適合用遞迴技巧來寫呢? 這份教材不打算多介紹遞迴的想法概念, 我們在下面兩節示範兩個典型的問題。 讀者如果學習演算法或資料結構,都有可能遇上更多的範例。
習題