2013年9月21日 星期六

Round 3: All bundled -- vector as object

Round 2回顧中找到的議題向量及其維度應能確保一致。我們將以C++的class加以解決。進入第三輪。

U步驟:我們需要一個策略由於我們的目的是減少programmer犯錯的機會,因此採取策略:檢視program code,找出容易導致programmer犯錯的code。

先看一個程式片段:



上面main函數讀起來,可以說是以u這個變數表示向量;和向量u的定義、使用相關的敘述有 
  • line 51: int dim_u; (向量的維度) 
  • line 52: double * u; (double 指標,指到向量記憶體中的位置)
  • line 55: u = new double[dim_u];(配置記憶體,佔用連續的dim_u個double,u為這塊記憶體的beginning address) 
  • line 71: delete [] u; (釋放u佔用的記憶體)
  • line 36 (and other lines): p += u[i]*v[i];(取用第i個分量)

簡單以下圖表示它們之間的關係:



我們發現programmer需自行關聯double vector[]、int dim;兩者也與 double * 宣告、new 運算子、delete運算子、與index 運算子 [] 等有關係,這些關係都得靠你自己記住;C幫不上忙! 「C是個相對低階的語言」這種說法,果然不是隨便說說的。

好消息:C++可以幫上忙!C++的class可以讓我們把這些構成向量的變數、變數間的關係變數與運算子間的關係做一個統合

壞消息:你得學會如何將散落各處的資料、函數打包(好吧,這算不上是一個壞消息 :) 

列出新的子問題如下:

SP3: 統合向量相關變數、運算、函數,以物件表達,修改相關函數,使其接受並使用向量物件。

當此問題解決後,programmer使用物件時不需自行關聯獨立的變數。

現在未解的問題有SP3 及 SP2,讓我們先解 SP3。
D步驟:如何列出工作?

T7 宣告新的向量型態,決定它打包的資料函數
T8 修改相關函數以使用新的向量型態

更新待辦工作如下:



C步驟先做T7。將新的類別型態(class type)稱為vector,經過U步驟地分析得知,
  • vector類別型態打包兩項資料double []與int (學習課題:class, data member, member functions, private, public)。
  • 定義vector類別型態變數的建構元(constructor) (學習課題:constructor, initialization list, dynamic memory allocation, zeroing)
  • 回收被vector類別型態變數佔用的記憶體用的解構元(destructor) (學習課題:scope and lifetime of a variable, destructor, memory deallocation)
  • 向量分量取用函數(accessor function)(學習課題:operator overloading, return by reference) 
  • 向量維度的讀取函數(getter function) (學習課題:const member function)
括弧中為C++ 相關的學習課題,你可以找任何一本C++書或任何C++ web site,直接閱讀與學習課題相關的頁數/內容,直到你可了解下面的code即可:

class vector {
private:
double * _v;
int _dim;
public:
vector(int dim):_dim(dim){
_v = new double[_dim];
for (int i=0; i<_dim; ++i)
_v[i]=0;
}
~vector() {
delete [] _v;
}
double & operator [](int i) const {
return _v[i];
}
int dim() const {
return _dim;
}
};

畫張類別圖(class diagram)來表示vector打包的資料與函數:





有了 vector class 型態,接著處理T8。

T8的學習課題:model of a variable in memory, runtime memory model of a program, call by reference vs call by value, copy constructor, deep copy vs shallow copy。

原 main函數片段



可修改如下:

新程式line 70

    vector u(dim_u);

呼叫建構元,定義vector型態的物件u,它的維度為dim_u;line 70 取代原程式line 51, line 52及line 55。

新程式line 71

    inputVector(u);

取代舊程式line 56,

    inputVector(u, dim_u);

呼叫inputVector時只需傳入vector型態的物件u,而不傳入它的維度dim_u。

完成的程式請點連結下載。

L步驟:請檢查round 3的程式碼是否優於前一版,讓programmer犯錯的機會減少。

除此之外,讓我們再次以programmer的觀點進行回顧。這次,回想自round 1起,我們為求方便,每當完成一個工作時,(例如round 1中的innerProduct函數)經常借用main函數來測試這個工作完成的code。(例如,準備好兩個向量,將作為參數他們傳給innerProduct函數做計算,然後將結果輸出到console:見round 3程式)

/*
double a[2]={0,1};
double b[2]={1,0};
double c[3]={1,2,3};
vector u(2,a);
vector v(2,b);
vector w(3,c);

double prod;
if (innerProduct(prod,u,v))
cout << "inner product is " << prod << endl;
else
cout << "Dimension error!" << endl;

if (innerProduct(prod,u,w))
cout << "inner product is " << prod << endl;
else

cout << "Dimension error!" << endl;
*/

注意到這些code被註解掉了嗎?因為後來我們需要 main 函數做它該做的事!在HTSI的U-D-C-L迴授圈中的C步驟,這些測試碼 (test code)擔負著驗證工作是否完成的責任,但是它們卻「寄生」在main函數之下。

我們找到一個議題:讓test code脫離 main函數存在

© Y C Cheng, 2013. All rights reserved.

沒有留言:

張貼留言