請讀者抓下這個檔案:
ex.m這是一個 Matlab 的 function,需要兩個向量當輸入,最多會有兩個輸出。 假設 C、D 是兩個向量,而且 C 比 D 長很多,當你下指令
[A B]=ex(C,D)A 是 C 的奇數項和 D 疊積的結果,B 是 C 的偶數項和 D 疊積的結果。 讀者不用在意疊積的定義,這不是本文的重點, 請直接看下面的說明就夠了。
這個程式的原始碼如下:
function [A,B]=ex(C,D) LN=length(C); SN=length(D); A=zeros(1,ceil((LN-2*SN+2)/2)); B=zeros(1,floor((LN-2*SN+2)/2)); for i=1:2:LN-2*SN+2 for j=1:SN; A((i+1)/2)=A((i+1)/2)+C(i-2+2*j)*D(j); end; end; for i=2:2:LN-2*SN+2 for j=1:SN; B(i/2)=B(i/2)+C(i-2+2*j)*D(j); end; end;因為這個程式用了大量的迴圈,所以當 C 或 D 的長度很長時﹝以百萬為單位﹞, 執行時間將會拖得很長。 為了改善效率,我們有必要利用 C 重寫這個程式。
這裡是同一個程式的 C 版本:
ex2.c請讀者下載編譯後依序輸入以下的指令:
現在,我們來看看 ex2.c 的原始碼:
#include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { int i,j,ln,sn; double *lv,*sv,*v1,*v2; if (nlhs>2) mexErrMsgTxt("Two return values"); if (nrhs!=2) mexErrMsgTxt("Require 2 inputs"); for (i=0;i<2;++i) { if (!mxIsDouble(prhs[i])) mexErrMsgTxt("Only for double value."); if (mxIsComplex(prhs[i])) mexErrMsgTxt("Not for complex value."); if (mxGetM(prhs[i])!=1) mexErrMsgTxt("Only for row vector."); } ln=mxGetN(prhs[0]); sn=mxGetN(prhs[1]); if (ln<sn*2-1) mexErrMsgTxt("Empty result."); plhs[0]=mxCreateDoubleMatrix(1,(ln-2*sn+3)/2,mxREAL); plhs[1]=mxCreateDoubleMatrix(1,(ln-2*sn+2)/2,mxREAL); lv=mxGetPr(prhs[0]); sv=mxGetPr(prhs[1]); v1=mxGetPr(plhs[0]); v2=mxGetPr(plhs[1]); for(i=0;i<(ln-2*sn+3);i+=2){ for(j=0;j<sn;++j) *(v1+i/2)+=(*(lv+i+2*j))*(*(sv+j)); } for(i=0;i<(ln-2*sn+2);i+=2){ for(j=0;j<sn;++j) *(v2+i/2)+=(*(lv+i+2*j+1))*(*(sv+j)); } }這裡用到了大量 Matlab 提供的函式。 Matlab 提供給 C 的函式分成兩類:
接著來介紹 mexFunction。 mexFunction 提供了四個參數:nlhs、plhs、nrhs 及 prhs。 在 nrhs 和 prhs 中存放著傳給 subroutine 的參數及參數的資訊 (rhs 就是等號右邊的意思 right hand side), 而 nlhs 和 plhs 則存放著要回傳給 matlab 的結果及關於結果的資訊 (所以 lhs 就是等號左邊)。
現在看一下這些參數代表的意義, nlhs 是呼叫 subroutine 時使用者希望得到的輸出數目, nrhs 則是傳進 subroutine 的參數數目。例如下指令
[A B C]=func(D,E,F,G)nlhs 會是 3,nrhs 會是 4。 nlhs 並不是由 subroutine 決定的, 而是使用者呼叫 subroutine 時由 Matlab 根據使用者的呼叫決定的。 這使得 subroutine 可以決定是否可以省略某些計算。 例入我們把上面的指令改成
[A B]=func(D,E,F,G)這時 nlhs 為 2,func 就可以選擇不算出
plhs 和 prhs 都是指向一個序列的指標, 這個序列放的都是指向 mxArray 結構的指標。 在 Matlab 中只有一種物件型別,就是 array。 所以 Matlab 在呼叫 C 寫成的 subroutine 時, 傳進去的要是 array,得到的回傳也必須要是 array。 在 C 裡面,Matlab 傳進來的 array 全部都是 mxArray 的結構, 而傳回 Matlab 的 array 也同樣都是 mxArray 的結構。 所以 plhs 和 prhs 實際上就是指向一堆要傳回去的以及要拿進來的 array。 plhs 指向要回傳給 Matlab 的 array, prhs 則是指向 Matlab 傳給 subroutine 的 array。 以
[A B C]=func(D,E,F,G)為例,prhs[0] 指向矩陣 D,prhs[1] 指向 E。 而 plhs[0] 指到的矩陣在 subroutine 結束後會自動變成矩陣 A。 所以只要為 plhs 配置好記憶體, subroutine 結束後 Matlab 會自動取得 plhs 指定的矩陣,而不用特別去回傳 plhs。 接著介紹一些用來操作 prhs 和 plhs 的函式。
在應用上,mxArray 內的資料型態多半會是 double,但是也會有例外。 如果希望知道一個 mxArray 是存放 double 的變數, 我們可以下指令 mxIsDouble(prhs[0]) (prhs[0] 是 mxArray* 型態), 如果 prhs[0] 指到的 mxArray 代表的是 double 的 array,則這個函式傳回 true, 否則傳回 false。
在 mxArray 內有兩個指標,一個是 pr,一個是 pi,分別指向兩個維度相同的矩陣。 pr 指向的是 array 的實數部分,pi 指向 array 的虛數部分。 當 array 是複數時,pr 和 pi 指向的兩個矩陣相加代表整個完整的 array。 當 array 是實數時,pi 會指向 null,這時 pr 就足夠代表整個 array。 如果想要知道一個 array 是否為複數,可以用 mxIsComplex。
當你想要得到 plhs[0] 指向的 mxArray 的 pr 指標,可以下指令
pr=mxGetPr(plhs[0]);如果想得到 pi 指標,可以下指令
pi=mxGetPi(plhs[0]);pr 和 pi 多半是 double* 型態, 藉由這兩個指標,我們可以對 plhs[0] 指向的 array 做修改。 如果這個 array 的維度是 m 乘 n, 則第 i 列第 j 行會在 pr+m*(j-1)+i-1 以及 pi+m*(j-1)+i-1。 有了這些資訊,剩下的就是對指標的操作了。
如果要知道一個 plhs[0] 指到的 array 的列數,可以下指令
mxGetM(plhs[0]);會傳回一個整數,表示列數。 而行數則可以用
mxGetN(plhs[0]);來得到。
在 ex2.c 中,我不但用到了上面介紹的函式,同時也用了 mexErrMsgTxt。 這是一個檢查用的函式,它會強制中斷 subroutine, 回到 matlab 並在 matlab 顯示錯誤訊息 (使用者自己寫入)。 配合上面介紹的函式,我對我的程式做了以下的限制:
現在還剩下一件事,就是如何建立一個新的 array。 通常我們會直接將建立好的 array (在 C 中會是 mxArray 結構) 交給 plhs。 這代表我們需要做記憶體的配置,這個函式也由 matlab 提供:
plhs[0]=mxCreateDoubleMatrix(1,(ln-2*sn+3)/2,mxREAL);mxCreateDoubleMatrix 會建立一個 double 型態的 array, array 的列數由第一個參數指定,行數由第二個參數指定。 第三個參數如果是 mxREAL, 則建立一個實數 array,mxCOMPLEX 則為複數 array。 mxCreateDoubleMatrix 會將指向這個 mxArray 的指標回傳, 到此一個 array 算是完全建立了。 這個新建立的 array 內的元素都是 0,所以我們不用再初始化這個 array。 李易霖 (2004/08/04) ---