就像數學有「向量值函數」,Matlab 也有「多值函式」。 更厲害的是,Matlab 可以檢查使用者想要幾個答案? 他可以根據使用者的不同要求而回答不同類型的答案。 這是 Matlab 語言比其他大多數程式語言 (例如 C 語言) 方便的地方。
例如當 A 是一個 3x4 的矩陣,則函式
p=size(A)傳回的答案是個 2 維序列,序列的第一個元素是 A 的列數, 第二個元素是 A 的行數;也就是
p = [3, 4]
Matlab 本來就是個陣列導向計算軟體,所以 Matlab 能夠得到序列或向量的答案並不稀奇。 有趣的是,您也可以要求 size() 傳回兩個變數,有如
[m,n] = size(A)則 m 和 n 分別是一個純量: m 是 A 的列數,而 n 是 A 的行數;也就是
p = [m, n]
可見 size() 被要求傳回一個變數 (p=size(A)) 或者被要求傳回兩個變數 ([m,n]=size(A)) 這兩種情況, 雖然回答的答案一樣,但是回答的方式不同。 這就是我所謂 Matlab 可以「根據使用者的不同要求而回答不同類型的答案」的意思。
再看一個例子。令 x 是一個序列,例如
x = [2 0 7 4 9 5];那麼,如果只要求一個回傳變數,則 sort() 內建函式就把 x 從小到大排序,例如
sorted = sort(x)得到 sorted 是 [0 2 4 5 7 9]。 順便提兩個要點:
tmp = sort(x);得到 sorted 是 [9 7 5 4 2 0]。
sorted = tmp(end:-1:1)
如果向 sort() 索取兩個回傳變數,則 sort() 的行為就有點改變。 例如
[p, q] = sort(x)則 p 就相當於剛才的 sorted,也就是 [0 2 4 5 7 9]。 而 q 就是原來 x 元素之足標被排序之後的新順序,也就是 [2 1 4 6 3 5], 留意看看,q 的元素數值是從 1 到 6 的整數,而且每個整數出現一次。 它的意思是說,x 排序之後的結果,是
[0 2 4 5 7 9] == [x(2), x(1), x(4), x(6), x(3), x(5)]換句話說,如果說
[sorted, ind] = sort(x);則 x(ind) 的重新排列結果就是 sorted。 簡單地說,sorted 給我們排序的結果, 而 ind 保留了排序前的足標號碼。
獲得了 ind 有許多好處。譬如現在 A 是一個代表成績簿的矩陣, 它的第一行代表學號,而後每一行代表一次作業或考試的成績。 假如第 7 行是期中考成績,那麼
[sorted, ind] = sort(A(:,7));其中 sorted 告訴我們期中考成績的排序,譬如最高分是 sorted(1) 而最低分是 sorted(end)。 而 ind 可以告訴我們,是「誰」得了最高分、「誰」得了最低分。 那就要用到學號那一行,所以 A(ind(1),1) 是得到最高分那人的學號, 或者 A(ind(7),1) 是排名第七的人的學號。
sorted = sorted(end:-1:1);
ind = ind(end:-1:1);
使用者自訂函式也可以檢查 caller 那端索取幾個回傳變數, 並據以改變本函式的行為。重點是 Matlab 的另一個內建函式 nargout() (number of output arguments)。 我們繼續上一節的 collatz.m,再修改它, 讓使用者可以索取一個或兩個回傳變數。 如果只索取一個,那就跟以前寫的一樣:collatz(x) 回傳以 x 為首的 Collatz 數列長度。 如果索取兩個,則第一個變數是數列長度, 第二個變數就是以 x 為首、直到第一個 1 為止的 Collatz 數列, 存在一個序列裡面。
整個原始碼寫在這裡,其基礎是前一節的原始碼。 為了方便閱讀,我們將新加入的原始碼用藍色字型顯示。
function [n, z] = collatz(x, arg2) % Usage: [n, z] = collatz(x, [arg2]) or n = collatz(x, [arg2]) % % Here x is a positive integer, let it be x_0 and generate the % Collatz sequence until the first n such that x_n = 1. Return n. % arg2, which is optional, specifies the max number of iterations. % The second return variable z, which is optional, is the actual % Collatz sequence computed to the first 1 or to the % end of iterations. % % Shann 2004-05-05 % Check input arguments if (nargin == 2) ITER = arg2; elseif (nargin == 1) ITER = 1000; else disp(['Error: must have one or two input arguments.']); return; end % Input validation: is a scalar, is an integer, is positive. if (length(x) > 1) disp(['Error: input must be a scalar.']); return; end if (floor(x) ~= x) disp(['Error: input must be an integer.']); return; end if (x < 1) disp(['Error: input must be a positive integer.']); return; end % Let x be x_0, start the Collatz sequence x_n, n=1,2,3... % until the first x_n = 1 n=0; if (nargout==2) z = x; end while (x>1) & (n<ITER) n = n+1; if (rem(x,2)) x = 3*x+1; else x = x/2; end if (nargout==2) z = [z, x]; end end
以上我們用了一個 Matlab 特有的技巧:在既有的序列後面添加元素的技巧。 如果使用者要求兩個回傳變數,collatz() 才著手準備 z 序列; 否則根本不產生 z 序列。 一開始的時候,z 並不是序列,它只是一個純量 x0。 但是,Matlab 其實將純量視為 1 乘 1 矩陣,同時也是 1 維序列、1 維向量。 然後,在迴圈內,我們用
z = [z, x];這個技巧,讓新計算出來的 xn 接續到現有的 z 序列後面。 譬如若 z 原來是 [3 10 5],而新計算出來的 x 是 16,那麼 [z, x]; 合併起來就是 [3 10 5 16]。 這是 [矩陣的合併] 那一節講解的技巧。
使用新版的 collatz(),如果說
collatz(3)或者
n = collatz(3, 100)都還是得到答案 7,但是現在可以看看以 3 為首的 Collatz 數列究竟如何:
[n,x] = collatz(3, 100);則 x 是 [3 10 5 16 8 4 2 1]。 萬一設定的迭代次數不夠,例如
[n,x] = collatz(3, 5);則只會算到 x5 為止, 此時 x 有六項,是 [3 10 5 16 8 4]。
Matlab 有個壞習慣:它一定要序列的足標從 1 開始。所以,collatz() 內 z(1) 存著的其實是 x0, 依此類推,z(k) 其實儲存 xk-1。 這是使用 Matlab 的一個不方便的地方。
習題