プログラミング・演習II 第 10 回 オブジェクトの拡張 BinaryTree 総称 アクセス制御 双方向リストとpriorityQueue Event 田向 前回までのデータ構造 スタック First In Last Out (FILO) キュー First In First Out (FIFO) メンバ関数は共通 push(char ch), pop(); メンバ変数は,スタックは現在位置を表す変数 キューは,push, popの位置を表す二つの変数 2進木: binaryTreeNode class BinaryTreeNode { BinaryTreeNode *left ,*right; int value; public: BinaryTreeNode(int newValue) {left = right = 0; value = newValue; } ~BinaryTreeNode() {…} //??? }; 2進木:binaryTreeNode使い方 ノードをキー(値)で並べる. 新しいノードをN, ツリー内の現在のノードをCとする. 1.Cをツリーの根とする. 2.Cが空でないとき – キー(N) < キー(C)ならCを左部分 木に設定. – キー(N) > キー(C)ならCを右部分 木に設定. – キー(N) = キー(C)ならエラー 3.位置CにNを挿入する. 2進木: binaryTreeNode 12 (新値) 8 20 2 1 13 3 9 21 15 12 class BinaryTreeNode { BinaryTreeNode *left ,*right; int value; public: BinaryTreeNode(int newValue) {left = right = 0; value = newValue; } ~BinaryTreeNode() {…} //??? }; 12 (新値) 8 20 2 1 13 3 9 21 15 12 1. 2. 3. 4. 5. 12< (8): false: cur->right≠0: move to right 12<(20): true : cur->left≠0 :move to left 12<(13): true : cur->left≠0 :move to left 12< (9): false: cur->right = 0 : set at right of (9) 問題 1.以下に示す10個の数字を,示された 順番で2分木へ格納しなさい 6, 4, 3, 9, 0, 1, 2, 5, 7, 8 2.上記ルールで作成した2分木を使い, 入力したデータを昇順に読み出せること を確認せよ binaryTreeNode Class BinaryTree { BinaryTreeNode * root; Public: BinaryTree ( ){ root = 0; } ~BinaryTree( ); int insert(int value); int lookup(int value); }; int BinaryTree::lookup(int value){ BinaryTreeNode * cur = root; while(cur){ if (cur->value == value) return 1; else if ( cur->value <value) cur = cur->left; else cur = cur ->right; } reuturn 0; } binaryTreeNode Class BinaryTree { BinaryTreeNode * root; Public: BinaryTree ( ){ root = 0; } ~BinaryTree( ); int insert(int value); int lookup(int value); }; int BinaryTree::lookup(int value){ BinaryTreeNode * cur = root; while(cur){ if (cur->value == value) return 1; else if (value < cur-> value) cur = cur->left; else cur = cur ->right; } return 0; } 2進木::insert int BinaryTree::insert(int value){ BinaryTreeNode *cur = root; if(cur==0){ // recursive call の終端処理 root = new BinaryTreeNode(value); return 1; } for(;;){ if(cur->value == value) return 0; //既にある. else if(value < cur->value){ /*辿る先があれば,辿る.*/ if(cur->left){ cur = cur->left; continue; } //for の最初から再実行 //行き止まりなので,そこにノードを作る. cur->left=new BinaryTreeNode(value); return 1; } else{ if(cur->right){ cur=cur->right; continue;} //行き止まりなので,そこにノードを作る. } cur->right=new BinaryTreeNode(value); return 1; } } 2進木の総称化 • BinararyTreeNode には整数値(int value)しか入 れられない. • 用途毎にcodeを書き換えなければ,ならない. • 解決法:クラスを総称クラスに変える. – オブジェクト自体ではなく,オブジェクトへのポ インターを保持する. class BinaryTreeNode { BinaryTreeNode *left ,*right; void * value; public: BinaryTreeNode(void* newValue) {left = right = 0; value = newValue; } ~BinaryTreeNode() { } }; – 未知の型のオブジェクトへのポインター – ふたつのオブジェクトを比較するすべがない. 比較関数 • 2進木に入れる物の比較を専用に用意する. • 関数へのポインタを取る. – 関数にも(先頭)アドレスがあるから. int (*function)(int , int ); 引数が二つで,intの帰り値をとる関数へのポインタ //typedef で関数ポインタ comparisonFunction を定義. typedef int (*ComparisonFunction) (void *, void *); class BinaryTree { BinaryTreeNode *root; ComparisonFunction _compare; public: BinaryTree(ComparisonFunction compare) {root = 0; _compare = compare; } ~BinaryTreeNode() { } virtual void *insert(void *value); virtual int remove(int value); void *lookup(void *value); }; 例1 • Btnodeに入れる物: 文字列 • 二つの文字列を比較する関数を定義. // 引数が二つで返り値がintの関数 int CompareString(void* v1, void* v2) { char *s1 = (char*) v1, *s2=(char*)v2; return strcmp(s1,s2); } • Btree を作るときに,この比較関数を指定す る. → そのアドレスを渡す. main(){ BinaryTree bt(CompareString); } ※復習:C言語で出たstdlib.hのqsort()の利用 方法と同じ 例2 • 作ったWindow画面をBinaryTree に入れる. //画面 class myWindow : public window { coord crdxyZ; int width, height; int winID; ….. } int CompareWindows( void* w1, void *w2) { coord* c1=w1->crdxyZ, c2=w2->crdxyZ; return c1->z < c2->z; } • zの値でwindowsの重なり具合を決定するコードが かける. • 総称の機能を,もっと,組織的に綺麗に 強力にやりたい. どうする? Templateの書式 Complex,coordinateのところを変数Tのようにしたい. template <class T> class array { T *p; int size; public: array(){ p = new T[10]; if(!p) exit(1); size=10; } array(int sz) { p = new T[sz]; if(!p) exit(1); size = sz; cout << "「通常の」コンストラクタを使う\n"; } ~array() {delete [] p;} array(const array &a); void put(int i, T j) { if(i>=0 && i<size) p[i] = j; } T get( int i){ return p[i]; } int getSize(){return size;} T &operator[](int i){return p[i];} }; Template Library が作れる. このarrayの呼び方: array<node> a; 程度が妥当. 汎用かつ強力,極めて重要な機能 けれど,単なる 約束,決まり!! クラスの継承 クラスの継承と合成とは異なる. 基本クラスから,下の記述で書かれる. class className : public classBase { //--public: //--} //node からANDを派生 class AND : public node{ //nodeと類似なもの input i1, i2; output o1; Public: ….. }; //AND,ORから halfAdder を合成 class halfAdder{ AND and1; //部分として組み込まれる OR or1 ; Public: ……. }; 基本クラスのアクセス制御 class derived-class-name: access base-class-name { //…… } access : public: 基本クラスの全ての公開メンバ (public)が派生クラスの公開メンバになる. private: 基本クラスの全ての公開メンバ (public)が派生クラスの非公開メンバ (private)になる. protected: 基本クラスの被保護メンバー (protected)は派生クラスのメンバーからも アクセスできる. 基本クラスのアクセス制御 public #include <iostream> using namespace std; class base { int x; public: void setx (int n) { x = n; } void showx() { cout << x << '\n'; } }; class derived : public base {// publicとして継承する int y; public: void sety(int n) { y = n; } void showy() { cout << y << '\n'; } }; int main(){ derived ob; ob.setx(10); // 基本クラスのメンバにアクセス ob.sety(20); // 派生クラスのメンバにアクセス ob.showx(); // 基本クラスのメンバにアクセス ob.showy(); // 派生クラスのメンバにアクセス return 0; } 基本クラスのアクセス制御 private #include <iostream> using namespace std; class base { int x; public: void setx(int n) { x = n; } void showx() { cout << x << '\n'; } }; class derived : private base {// publicとして継承する int y; public: void sety(int n) { y = n; } void showy() { cout << y << '\n'; } }; int main(){ derived ob; ob.setx(10); // 基本クラスのメンバにアクセス × ob.sety(20); // 派生クラスのメンバにアクセス ob.showx(); // 基本クラスのメンバにアクセス × ob.showy(); // 派生クラスのメンバにアクセス return 0; } 基本クラスのアクセス制御 protected #include <iostream> using namespace std; class samp { int a; // デフォルトにより非公開 protected: // samp以外には非公開 int b; public: int c; samp(int n, int m) { a = n; b = m; } int geta() { return a; } int getb() { return b; } }; int main(){ samp ob(10, 20); // ob.b = 99; エラー! bはprotected。したがって非公開 ob.c = 30; // OK。cは公開 cout << ob.geta() << ' '; cout << ob.getb() << ' ' << ob.c << '\n'; return 0; } protectedは派生ではアクセス可 #include <iostream> using namespace std; class base { protected: // 基本クラス以外には非公開ながら int a, b; // 派生クラスからはアクセスできる public: void setab(int n, int m) { a = n; b = m; } }; class derived : public base { int c; public: void setc(int n) { c = n; } // この関数は、基本クラスのaとbにアクセスできる void showabc() { cout << a << ' ' << b << ' ' << c << '\n'; } }; int main(){ derived ob; /* aとbは、ここではアクセスできない。 基本クラスとその派生クラス以外には非公開 */ ob.setab(1, 2); ob.setc(3); ob.showabc(); return 0; } protectedは派生ではアクセス可 class base { protected: // 基本クラス以外では非公開ながら int a, b; // 派生クラスからはアクセスできる public: void setab(int n, int m) { a = n; b = m; } }; class derived : protected base { // protectedとして継承 int c; public: void setc(int n) { c = n; } // この関数は基本クラスのaとbにアクセスできる void showabc() { cout << a << ' ' << b << ' ' << c << '\n'; } }; int main(){ derived ob; // エラー:setab()は基本クラスの被保護メンバ ob.setab(1, 2); // ここからはsetab()にアクセスできない ob.setc(3); ob.showabc(); return 0;} constructor,destructor の呼び出し順序 #include <iostream> using namespace std; class base { public: base() { cout << "baseクラスのコンストラクタ呼び出し \n"; } ~base() { cout << "baseクラスのデストラクタ呼び出し\n"; } }; class derived : public base { public: derived() { cout << "derivedクラスのコンストラクタ呼び出 し\n"; } ~derived() { cout << "derivedクラスのデストラクタ呼び出 し\n"; } }; int main(){ derived o; return 0; } 呼び出し順序 Base → derived → derived → Base 多重継承 class derived-class-name: access base1, access base2,・・・,access baseN { //…… } Derived-const(arg) : base1(arg1), base2(arg2), ・・・,baseN(argN){ //….. } いくつもの基本クラスから派生できる. 初期化子 “:” と “{” の間に置く 仮想基本クラス class derived-class-name: virtual access base-class-name { //…… } virtual 仮に宣言する.処理は後で 基本クラスが重複した場合,複数のコピーが生じるのを防ぐ. class base… class derived1: public base …. class derived2: public base ….. class derived3: public derived1, public derived2 ….. base のコピーがderived1 derived2 からのが二つあ る. 必要になったときによく読んで理解する. 今は,あるということだけ知っていれば良い. 問題 1.10-1.cpp をコンパイルしてみよ.問題があ る場合,なぜなのかを指摘して,正しく動 作するようにmain関数で問題がある部分 をコメントアウトして出力がどうなるか確認 せよ. 2.10-2.cppをコンパイルしてみよ.問題があ る場合,なぜなのかを指摘して,正しく動 作するようにmain関数内を書き換えよ. 3.10-3.cppをコンパイルしてみよ.問題があ る場合,なぜなのかを指摘して,正しく動 作するようにderivedクラスを書き換えよ. 4.10-4.cppをコンパイルして動作させよ.ど のような順番でコンストラクタとデストラクタ が呼び出されているか確認せよ.さらに, コンストラクタの引数がどのように伝わって いるか確認せよ. 離散イベント: event class Class event( int index, class e) • 何かの出来事e.離散的,個別的で – indexで区別をつけることが出来る. – 順序付けられると整理しやすい. • 例) – 信号の変化: (t1,1->0),(t2,0->1),… – 命令の順序: (t1, inst1), (t2,inst2),… – 図の位置: (p1, ◎),(p2, □),… • event e1が発生,t1後に処理 – (10, e1) , (5,e2), (12, e3), (2,e4) – (2,e4), (5,e2), (10,e1), (12,e3) と並べ替 える必要がある.単純にqueueに積め ない. event class の実装 class event{ int eCode; public: int time; event( ){time=0; eCode=0;} event(int t, int e1){ time=t; eCode=e1;} }; イベントをノードに積む. class node{ public: node* left; node* right; int index; event e; node( ){left=right=0; index=0;} node (node* l, node* r, int i){ left=l; right=r; index=i;}; }; queueの改良 push する時に,indexで入れる位置を探し て挿入する. Indexで並べてしまうqueueをpriority queue という. push(t,e) 1. t でqueueを調べて入れる場所を探す 2. 入れる場所に空きが無ければqueueの要 素をずらして場所を作る. 3. 出来た場所にeをコピーする 4. tos1,tos2を更新する. 5. queueがいっぱいになったとき困る.エ ラー処理もいる. priority queue insert( )が頻繁に起こる. いつも要素をずらしているととても遅くなる. array が配列構造ではうまくいかない. 双方向リストで実現しよう. class list{ node* root; int size; public: list( int s ){ node *pl, *pr; root = pl = new node( 0, 0, 0); size=s; for(int I=1; I<s;I++) { pr=new node(pl,0,I); pl->right=pr; pl=pr;} } node& operator[ ](int j); }; priority queue-II node& list ::operator[ ](int j) { node* p=root; for(int i=0; i<size; i++){ if(p->index<j) p = p->right; else if(p->index==j) return *p; else { if(p->index>j) { node* p1 = new node(p, p->right, j); (p->right)->left=p1; p->right = p1; return *p1; } } } } プログラムへの組み込み class priorityQueue{ list queue; public: priorityQueue():queue(10){ } push(event& e1){ queue[e1.time].e=e1;} event& pop(int i) { return queue[i].e; } }; main(){ priorityQueue EVH; //event handler event e1(0,1); EVH.push(e1); event cEvent; cEvent=EVH.pop(0); cout << cEvent.time ; } binaryTree + priority queue I List の替わりに binary tree で実現する class BinaryTreeNode { BinaryTreeNode *left ,*right; void * value; public: BinaryTreeNode(void* newValue) {left = right = 0; value = newValue; } ~BinaryTreeNode() { } }; Class BinaryTree { BinaryTreeNode * root; Public: BinaryTree ( ){ root = 0; } ~BinaryTree( ); int insert(int value); int lookup(int value); }; binaryTree + priority queue II typedef int (*ComparisonFunction) (void *, void *); class BinaryTree { BinaryTreeNode *root; ComparisonFunction _compare; public: BinaryTree(ComparisonFunction compare) {root = 0; _compare = compare; } ~BinaryTreeNode() { } virtual int insert(void * value); virtual int remove(int value); int lookup(void *value); }; binaryTree + priority queue III int BinaryTree::lookup(void* value){ BinaryTreeNode * cur = root; while(cur){ if (cur->value == value) return 1; else if ( _compare (value , cur-> value) ) cur = cur->left; else cur = cur ->right; } return 0; } binaryTree + priority queue IV int BinaryTree::insert(void* value){ BinaryTreeNode *cur = root; if(cur==0){ // recursive call の終端処理 root = new BinaryTreeNode(value); return 1; } for(;;){ if(cur->value == value) return 0; //既にある. else if(_compare (value , cur->value)){ /*辿る先があれば,辿る.*/ if(cur->left){ cur = cur->left; continue; } //for の最初から再実行 //行き止まりなので,そこにノードを作る. cur->left=new BinaryTreeNode(value); return 1; } else{ if(cur->right){ cur=cur->right; continue;} //行き止まりなので,そこにノードを作る. } cur->right=new BinaryTreeNode(value); return 1; } } binaryTree + priority queue V class priorityQueue{ BinaryTree queue; public: priorityQueue(ComparisonFunction compare): queue(compare){ } push(event& e1){ queue[e1.time].e=e1;} event& pop(int i) { return queue[i].e; } }; int Compare0(void* v1, void* v2) { int *s1 = (int*) v1, *s2=(int*)v2; return ( *s1 < *s2 ); } main(){ priorityQueue EVH(Compare0); //event handler event e1(0,1); EVH.push(e1); event cEvent; cEvent=EVH.pop(0); cout << cEvent.time ; } 現在までの構造 双方向リストで伸び縮みするarrayが出来た. Arrayに要素が無い場合には,新しく要素を 挿入することが出来る. 問題点:要素の探索が常に0から始めるので 遅くなる.しかし,当面は使える. BinaryTree を使えばもっと早くなる. Event:何でも良い,indexとして時間や,位 置を使えば,いろんなものを表現すること が出来る. priorityQueueに eventをしまうことが出来る ようになった. EventをBASEにして,派生eventを作ればよ い. オブジェクト再考 Classで色々なオブジェクトが作れる. オブジェクト間での値,命令,メッセージなどはeventと して扱うことが出来る. 座標系(x,y)をBinaryTreeでならべれば, オブジェクトを配置することも出来る. • 内部に座標系を作り,その領域をcoutに表示すれ ばよい. – myWindowの領域に書き込んで表示すれば よい. • オブジェクトとして, windowの画面(ソフト)以外で も良い. – カメラ(cmos センサー),マイク, – サーボ・モータ: ロボットの手足, – 動的な変化,事象(イベント),抽象的な物 • イベントとして,画像,音,ロボットへの命令,画面 の表示など,みな同じように扱える. • FPGA内の回路もオブジェクト, • 制御信号,データ,命令もイベント • 画像オブジェクトを作ってライブラリにする. オブジェクトの利用法 • 派生クラスの仕組み:基本的・共通・単純 なものから特定用途・専用・複雑なものを 作ることが出来る • Template,総称の仕組み: 共通の枠組 みを作り,扱う対象を自由に変えることが できる. • ライブラリを作ることが可能になる. – 共通で基礎的なライブラリから多種多 様な専用化が組織的にできる. STL(Standard Template Library) • 半端・冗長である欠点を克服した • 巨大・複雑なものが作成可能になった (例) 画像で使う部品 色々ある – gadget – Visual component library Builder,visual C++ 画像ライブラリが用意されてある. #include <Classes.hpp>,#include <Controls.hpp> #include <StdCtrls.hpp>,#include <Forms.hpp> #include <DBGrids.hpp> #include <Grids.hpp>,#include <DBCtrls.hpp> #include <Mask.hpp>,#include <ExtCtrls.hpp> #include <Menus.hpp> 画像部品をFormに貼り付けて画像を設計 これらはすべてclass ,オブジェクトである プログラム作成画面にコードを記述 クラスとメンバーが自動で 作成される ハードもソフトもすべてオブジェクト • オブジェクトにすれば,swとhwの区別が 無くなる. • それぞれの処理速度,情報が異なるが ディジタルではすべてバイナリ表現 • 相互のやり取りはイベントとする. ・ ・ ・ ・ image = cameraObject->getView( ); 30 flames/sec ・ ・ ・ ・ wtImage = waveletObject->Trans(); ・ ・ ・ ・ While( value > threhold){ ・ ・ ・ ・ Value = TempletObj>matching( level); 20ms ・ ・ ・ ・ } Should be 30ms ソフトでは6秒掛かる HDL記述で回路もソフト • プログラムと同様に記述して動かす • オブジェクトと同じような概念 ソフトと同じようにシミュレーションで検証 • VDECのEDAツールを使用. • 記述を修正,テストを繰り返す. 回路を合成する FPGAに書き込む回路情報がファイルとして作られる 回路:bit stream data file このファイルをclassのデータとする. 回路のオブジェクトを Class hwObject{…}とする Classのconstructorでobjectを作るときにこ の回路をFPGAにロードする. 回路とのやり取りは event として処理. プログラムの中から直接回路を制御すること が出来る. class hwObject{ bitStream bsc; … } 回路の動作: 値の変化イベントの列
© Copyright 2024 ExpyDoc