int - みる会図書館


検索対象: 月刊 C MAGAZINE 1993年7月号
61件見つかりました。

1. 月刊 C MAGAZINE 1993年7月号

表計算法によりフイボナッチ数列を求めるプログラム / * 表 : 初期値 0 を利用している * / / * プロトタイプ宣言 * / / * フィポナッチ数列 ( 素朴版 ) * / ください。実は , この辺が再帰呼び出しを 上手に使うコツて、 , なまじスタックを考え たり , 呼び出しの過程を追ったりするとか えって頭が混乱してしまいます。 次に表計算法による高速化の効果につい て検討します。 fib ( 29 ) に要する時間を測定 したところ , 約 8 ミリ秒て、した。同じ計算を するのに素朴な再帰版て、は 14.2 秒 ( TabIe 1 参照 ) て、したから , 1000 倍以上の高速化とい うことになります。そして , n が大きくなる と , この差はさらに劇的に開いていきます。 表計算法の目覚ましい効果がおわかりいた だけると思います。 、 , List 3 int tab [ 100 ] ; 1 ーっ 0 り 0 -4 L.0 6 行ー 8 0 レ 0 1 より 0 ワ 0 -4 eo ー 8 9 0 ー 1 00 っ 0 -4 1 よ 11 1 人 1 11 -1 よ 1 よ 1 人 11 1 人ワ朝ワワ 0 ワ 0 り 0 ワ e ー se re tu rn f i b (n ー 1 ) + f i b (n ー / * n く 0 の場合のチェックは省略した * / / * フィポナッチ数列 ( 表計算法 ) * / / * 計算済みの場合 * / / * 表の値を返す * / / * フィポナッチ数列の計算 * / / * 表に格納する一方で * / / * 求めた値を返す * / if (tableCn] ! = の return tableCn] : else { i n t f = f i b_na i ve (n) : table[n] return f; 前計算法 前の節て、は「計算する過程て、表を作ってい く」という方針を取りました。しかし , 表の 作り方はこればかりて、はありません。計算 に先立って , 表をあらかじめ作っておくと いう方針が有効な場合もあります。 フイボナッチ数列の場合て、いえば , List 2 に出てくる配列 table はフィボ、ナッチ数列の 一覧表て、すから , それをあらかじめ作って しまうのて、す。結論からいえば , このほう が計算は単純になります。 List 4 にそのアル ゴリズムを示しますが , この中て、 List 4 て、 A とマークした部分がフィポナッチ数列計算 の本体て、す。取り敢えず表を作ることに専 ことによって , 計算手順がずいぶん いケースには , 連想計算法のほうが簡単に 厳密に決めようとすると難しいのて、す。フ インプリメントて、きます。一方 , フィボ、ナ ィポナッチ数列の場合て、いえば , long て、計 と簡略化されていることがおわかりいただ ッチ数列の場合 , 計算手順は前計算法のほ 算て、きる範囲は n = 45 が限界て、す。このた けると思います。 うがより単純て、すから , 可能ならば前計算 め , 表の大きさは 40 としておきました。 40 List 4 のように , 計算に先立って表を完成 くらいまて、て、あれば , 余裕を持って計算て、 される手順のことを一般には「前計算法」と 法を採用したいものて、す。 きるという意味て、す。 いいます。以前紹介した Boyer-Moore のア , こて、前計算法 , すなわち List 4 に基づい ルゴリズムも前計算法の一種て、す。これに この大きさ 40 の表を埋めるのに要する演 たプログラムを List 5 に示します。このプロ 算時間を測定したところ , 1 ミリ秒以下て、し 対して , 前の節て、説明したように , 関数が グラムて、は , 前もって表をすべて埋めてお た。連想計算法の場合は , fib(29) を計算す 呼ばれるたびに引数と結果を表に記憶する くという手順を取っていますから , そこだ けを見ると速度的には不利て、す。一方 , 前 るのに 8 ミリ秒かかりましたから , フィポナ 方法を「連想計算法」と呼びます。 前計算法と連想計算法のどちらを採用す ッチ数列の場合は , 連想計算法よりも前計 述したように計算手順が単純なことは , 速 るのがよいかは , ケースパイケースて、異な 度的に有利になるはずて、す。 算のほうが , 演算時間 , プログラムの単純 ります。フィポナッチ数列のように , あら 前計算法て、は , 表の大きさに比例して実 さという点て、優れていることになります。 かじめどこまて、表を作ればよいかわからな 「高速化したかったら単純さを保とう」とい 行時間がかかりますが , この表の大きさを 表計算法 ( 2 ) によりフイボナッチ数列を求めるアルゴリズム int tab [ 表の大きさ ] : 2 : 3 : i n i t ー f i b ( ) table[0] = table[l] 5 : for ( i = 2 : i く表の大きさ : i + + ) 6 : table[i] 7 : 9 : 10 : fib(n) = table[i ー 1 ] + table[i return table[n] : 88 C MAGAZINE 1993 7

2. 月刊 C MAGAZINE 1993年7月号

p が 0 より小さい , コンヒ。ュータって , 本当に融通がききま やる必要があるのて、す。「降水確率は 0 ~ 100 の間て、すよ。」というのはエラーメッセージ または せんね。プログラムを書くというのはこの p が 100 より大きい 融通のきかない相手 , コンヒ。ュータに手と の一種といえるて、しよう。 という条件になるのて、す。「または」という り足とり作業の手順や細かい条件を教えこ List 2 を修正したのが List 3 て、す。 List 2 のはふたつの条件を結びつけ , そのふたっ む作業にほかなりません。慣れるまて、はな と List 3 の違いはどこにありますか ? Lis のうち , どちらか一方て、も ( 両方て、も ) 成り かなかたいへんな作業になりますが , 一歩 t 3 のポイントは初めの if の条件にありま 立っていればよい という新しい条件を作 一歩進んて、いきましよう。 す。 るものなのて、す。 さて , List 2 の間題点をかたづけてしまい 100 く p ).. if (p く 0 Ⅱ もう一度いいましよう。 この条件はいままて、見たパターンと違いま ましよう。 List 2 の間題点はなんだったかと いうと , 「降水確率が 0 より小さいときに す。不思議な縦棒い ) がふたっ並んて、いま if ( 条件 1 Ⅱ条件 2 ) { ューザに誤った入力て、あることを知らせて す。 PC ー 9801 のキーボードや画面上て、は " げ 処理 A くれない」ということて、した。降水確率を入 はなく " げとなっています。この縦棒ふた 力するときに手がすべって 1500 とかー 10 と つ ( Ⅱ ) は , 日本語て、いえば「または」という か , ありえない値を人間というものは入力 意味を持っ演算子て、す。つまり , してしまうものて、す。プログラムはそれに p く 0 Ⅱ 100 く p 対して適切なエラーメッセージを表示して は , 日本語て、読むと , Fig. 3 List 1 の実行結果 A> lcc listl. c 11d @link. i A 〉 listl 降水確率を入力してください : 50 降水確率は 50 % です。 傘を忘れずにね。 いってらっしゃい。 A 〉 listl 降水確率を入力してください : 0 降水確率は 0 % です。 傘はいりません。 いってらっしゃい。 A 〉 listl 降水確率を入力してください : 100 降水確率は 100 % です。 傘を忘れずにね。 いってらっしゃい。 A> listl 降水確率を入力してください : 1500 降水確率は 1500 % です。 傘を忘れずにね。 いってらっしゃい。 else { 処理 B 傘プログラムの完成 List ー LISTI ℃をコンバイル ー凵 STI . EXE を実行 LISTI . EXE の実行結果 ー凵 STI . EXE を実行 LISTI . EXE の実行結果 ー凵 STI . EXE を実行 LISTI . EXE の実行結果 ー凵 STI . EXE を実行 LISTI . EXE の実行結果 1 : #include く stdi0. h> 2 : #include く stdlib. h> 3 : 4 : #define MAX—LINE 5 : 6 : main() buf[MAX_LINE] : Char 8 : int p; 9 : 10 : printf ( ”降水確率を入力してください : " ) ; 11 : gets(buf) : 12 : p = atoi (buf) : 13 : printf ( " 降水確率は Xd % % です。 \ 心 , p); 14 : if (p く 0 Ⅱ 100 く p) { 15 : 100 の間ですよ。 %n ” ); printf( ”降水確率は 0 17 : else if (P 〉 = 5 の { printf ( " 傘を忘れずにね。 *n"); 19 : 20 : else ( printf(" 傘はいりません。 *n"); 22 : 23 : printf ( " いってらっしゃい。” ); 24 : 128 間違い探し List 1 : #include く stdiO. h> 2 : #include く stdlib. h> 3 : 4 : #define MAX—LINE 5 : 6 : main() bufCMAX LINE] : 8 : Char 朝 9 : int p; 10 : printf ( " 降水確率を入力してください : " ) ; 11 : gets(buf) ; 12 : 0 = atoi(buf) ; 13 : printf(" 降水確率は Xd % % です。 p); 14 : if (p く 0 凵 100 く p) { 15 : 100 の間ですよ。 *n"); printf(" 降水確率は 0 16 : else if (p = 〉 5 の { 17 : printf ( " 傘を忘れずにね。 }n"); 18 : else { printf(" 傘はいりません。 }n"); 20 : 21 : printf ( " いってらっしゃい。” ): 22 : 23 : } Fig. 4 List 2 の実行結果 A> lcc list2. c 11d @link. i A 〉 list2 降水確率を入力してください : 1500 降水確率は 1500 % です。 降水確率は 0 1 圓の間ですよ。 いってらっしゃい。 A 〉 list2 降水確率を入力してください : ー 10 降水確率は一 10 % です。 傘はいりません。 いってらっしゃい。 128 ー凵 ST2 ℃をコンノヾイル ー凵 ST2. EXE を実行 凵 ST2. EXE の実行結果 ー凵 ST2. EXE を実行 凵 ST2. EXE の実行結果 82 C MAGAZINE 1993 7

3. 月刊 C MAGAZINE 1993年7月号

PC -9801 シリーズドライプ情報表示ユーティリティ List 数だけ作成されます。たとえば , LASTDRIVE=Z と指定すれば A : —Z : の 26 個が起動時に 確保されます。使用していないものは空 き状態となっています。 SUBST コマンド が LASTDRIVE コマンドて指定したドラ イプ以降のドライプに置換がてきないの は該当する内部ドライプ情報を作成て、き ないためてす。 余談になりますが , この内部ドライプ情 報を書き換えてしまえば IBM PC 互換機て ードディスクを A : ドライプにしたり , P C ー 9801 シリーズてハードディスクを C : ドラ イプにするといったことが簡単にて、きてし まいます。いろんなマシンを使用していて ドライプ構成が異なると , しばしば混乱し ますが , 内部ドライプ情報を書き換えてド ライプ構成を合わせておけば便利かもしれ ません。 それて、は , 内部ドライプ情報を表示する プログラム例を drvinf. c ( 付録ディスク収録 ) に その実行例を Fig. 3 に示します。 PC -9801 シリーズの ドライフ情報 PC ー 9801 シリーズのユーザから SCSI, SA SI などのドライプ情報を知りたいという質 問を何回かいただきました OSCSI,SASI な どのドライプ情報というのは , どうしても 機種依存してしまうため , 長い間保留状態 にしておきましたが , 今回っいて、に紹介し ておきます。 PC ー 9801 シリーズて、ドライプ情報を知るに は , DA/UA リストを参照します。 DA(De vice Address) は , デバイスの種別を表す番 号て、 , UA(Unit Address) は , そのデバイス のユニットを表す番号て、す ( Table 3 ) 。上位 4 ビットが DA て、 , 下位 4 ビットがユニット番 号になっています。 この DA/UA のリストは , MS-DOS のシス テム変数領域の 60 : 006Ch&007Bh に A : —P : ドライプの情報がセットされていま 1 : # i ncl ude く s td i 0. h 〉 2 : # i ncl ude く s td ⅱ b. h 〉 3 : $include く dOS. h> 5 : void dspdrive98( void ) : 6 : void far *p98daua( void ) : 7 : void scsidrive( void ) : 8 : void sasi-fd( void ) ; 9 : 10 : int main( VOid ) printf( " PC -9801 シリ - ス・ト・ライフ・情報 \ n " ) : dspdrive98(); / * PC -9801 シリ - ス・のト・ライフ・情報表示 * / scsidrive(); / * SCSI ト・ライフ・情報表示 sasi_fd(); / * SASI, FD ト・ライフ・接続状態表示 * / return( 0 ) ; 19 : typedef struct { / * DA ( テ・ハ・イス種別 ). UA ( ユ : ット番号 ) リスト unsigned char bdaua[ 26 ] : 20 : / * DA/UA リスト unsigned short vdaua[ 26 ] : / * 上位ハ・仆 : DA/UA リスト unsigned char rsv[ ] : 22 : 23 : } S98DAUA; 25 : $define INTDC 0xdc 26 : 27 : void dspdrive98( void ) static char *pdauansg[] 29 : 30 : unknovn 32 : unknovn 33 : unknovn 34 : unknovn 35 : unknovn ” 320KB FD ” . / * 320KB FD 36 : 37 : unknovn " 640KB FD". / * 640KB FD. 両用 FD 640KB I/F ←ト・の 640KB ! * / 38 : 39 : FD アクセスモ - ド * / ” SASI HD" / * SASI HD ( 絶対セクタ番号指定 ) 40 : ヨ MB FD ” / * IMB FD, 両用 FD 1 MB I/F モ - ドの 1 M B FD * / 乃セスモ - ト・ * / ” SCSI" / * SCSI HD ( 絶対セクタ番号指定 ) ” unknovn 45 : / * SCSI テ・ハ・イス unknovn 46 : "unknovn 47 : ” RAM disk ” 48 : / * 両用 FD 640KB レ F モ - ト・の IMB FD アクセスそ - ト・ * / unknovn 50 : ( unsigned char far * ) 0X60006C し unsigned char far *pbdaua : 51 : / * DA/UA リスト 52 : S98DAUA far *pdaua; / * DA/UA リストへのネ。インタ 53 : int drvno; 54 . printf( "Yn DA/UA UnitYn" ) : 55 : pdaua : p98daua() : / * DA/UA リストの取得 56 : i f ・ ( pdaua ! : NULL ) { fo 「 ( d 「Ⅶ 0 : 0 , 58 : d 「Ⅷ 0 く sizeof( pdaua->vdaua ) / sizeof( pdaua->vdaua[ 0 ] ) : 59 : d 「 vno + 十 ) { 60 : pdaua->vdaua[ drvno ] : unsigned short vdaua / * 上位ハ・仆 : DA/UA リスト 62 : if ( vdaua 63 : / * 未使用 continue; printf( "Xc: % 04X X3u %sYn" / * ト・ライフ・名 drvno + ・ A ・ 66 : / * 上位ハ・仆 : DA/UA vdaua, ( vdaua 〉〉 8 ) & 0X0 「 . / * UA ( 北外番号 ) 68 : pdauansgC vdaua 〉〉 ] ) : 69 . 74 : 75 : 77 : 78 : 79 : 80 : 82 : 83 : 84 : } 85 : / * DA/UA リストの取得 86 : void far *p98daua( void ) / * DA/UA リスト 88 : static S98DAUA sdaua; 89 : union REGS iregs: void far *pintdc : ( void ね「 * ) ( INTDC * 4 ) : 90 : if ( pintdc : NU し L ) 92 : return( NULL ) : 93 : iregs. h. CI : 0 x 12 : イ * PC-9801 シリ - ス・のト・ライフ・情報表示 * / / * SASI HD ( リ : ア・セクタ番号指定 ) / * 両用 FD IMB レ F ←ト・の 640KB FD アクセスそ - ド * / / * SCSI HD ( リ : ア・セクタ番号指定 ) } se { fo 「 ( drvno : 0 : drvno く燔 : d 「 vno + + ) { unsigned char bdaua ニ pbdaua[ drvno ] : if ( bdaua / * 未使用 continue; %syn ” printf( "%c: % 02X X3u / * ト・ライフ・名 drvno + ・ A ・ / * 上位ハ・仆 : DA/UA bdaua, / * UA ( ユ : ット番号 ) bdaua & 0x0f. pdauamsg[ bdaua > > 4 ] ) : / * DA/UA / * PC -98 シリ - ス・以外 / * MS-DOS 製品番号の取得 98 C MAGAZINE 1 3 7

4. 月刊 C MAGAZINE 1993年7月号

0 囚朝朝朝 五ロ 0 に匱 結城浩 皆さんの生活の中でふたつ以上の選択にせまられると いうことか , おそらく多く存在すると思います。同し ようにプログラムの中でも条件に応してふたつ以上か ら選択するということか出てきます。今回はプログラ ム中での選択をテーマに話をすすめます。 ーーーもしもの国のアリス 第回 3 【文章 A 】 もしも , 降水確率が 50 % 以上 て、あれば , 傘を持っていく となります。この文章のうち「降水確率が 50 じようけん % 以上て、ある」の部分を条件といいます。ま た実際に「降水確率が 50 % 以上になる」こと を「条件を満たす」あるいは「条件が成り立つ」 といいます。 このような「もしも・・・て、あれば」というい い方に慣れている人はどんどん先を読み進 んて、かまいせん。しかしもしあなたが「もし も・・・て、あれば」といういい方に慣れていない 場合には , ちょっとここて、一休みして , 上 の文章をゆっくり眺めてください 「もしもナニナニて、あれば , コレコレ」と いう文章は何を主張しているのて、しようか。 いつもコレコレなわけて、はなくて , ナニナ ニという条件を満たしたとき初めてコレコ ・・と読めます。私はいつも傘を持っ ていくわけて、はなくて , 降水確率が 50 % 以 上のとき初めて傘を持っていくのて、ある・・ というわけて、す。 あたりまえのことをくどくどいっている ように聞こえますか ? しかしまだ慣れて いない C 言語て、考える前に , よく慣れている ②変数に値を代入する まじめに ③変数の値を参照する それから , 変数には「型」というものがある この原稿を書いているのは 4 月の末 , 連休 ことも学びました。 前て、す。今年の 4 月号 , つまり特集の「ゼロ プログラム中に変数が登場したら , 必ず から学ぶ C 言語入門」が掲載された号に対す 「この変数の型は何か」と調べる癖をつけよ る読者の皆さんのアンケート葉書が私のと うという話をしました。 ころに届きました。たくさんの励ましのお 次の 3 行が説明てきれば OK て、す。 便り , ありがとうございます。 int X 「わかりやすかった」「これから C を学ぶの て、よろしく」というお便りや「て、きるだけゆ printf ("%d*n", x) ; っくり進んて、ください」「おいていかないて、」 言て、説明すると , 上から順に 整数型の変数 x を定義 という訴えが多く見られました。また , 自 x に 3 を代入 分が学ぶためて、はなく「新入社員用の読み物 x の値を参照し , 表示 として」「教育用の参考に」という読者の方も いらっしやるようて , 私のほうがなるほど となります。よろしいてすか。 と思わされるお葉書もありました。皆様か マ月のポイント らのアンケート葉書は , て、きるかぎり反映 させていきたいと考えているのて、 , どうぞ よろしくお願いいたします。 今月は「もしもの国のアリス」と題して , 条。筰券について学ぶことにします。 レビューコーナー 先月は変数について学びました。変数に ついてのポイントが三つありましたね。覚 えていますか。 ①変数を定義する 78 C MAGAZINE 1993 7 日本語の「もしも・・・であれば」 まずは日本語て、考えてみます。 あなたは , 朝 , 天気予報を聞いて , 降水 確率が 50 % 以上なら傘を持っていくとしま しよう。このあなたの行動を整理すると ,

5. 月刊 C MAGAZINE 1993年7月号

たときには 0 以外を返してきますから , その ときにはウインドウをクローズし , そうて、 なければおそらくューザの気が変わったの て、しようからクローズしてはいけません。 クローズの処理自体はメニューから Close が 選択されたときと同じて、す。 ードラッグ マウスポタンが drag リージョン ( タイトル ノヾーのうち , クローズボックスとズームボ ックスを除いた部分 ) の中て、クリックされた ときは DragWindow を呼び出します。 Drag Window はボ、タンが押されている間 , マウス の動きに従ってウインドウのアウトライン を表示します。リターンしてきたときには すて、にウインドウは移動されています。 今回のサンプルプログラムて、は FindWin dow を呼び出した後に partCode て、 switch を 実行していますが , 各 case の中て、 , if (w ! = FrontWindow ( ) ) というふうにアクテイプウインドウかどう かを調べています。ところが inDrag のとき にはそれがありません。なぜかというと , 「アクテイプて、ないウインドウのタイトルバ ーを Command キーを押しながらドラッグす ると , そのウインドウは移動するがアクテ イプにはならない」という規定があるためて、 す。アクテイプて、ないウインドウのめんど うはウインドウマネージャが見てくれます が , これを実現するためにはアクテイプて、 ないウインドウだからといって SelectWind ow は呼び出してはいけません。 ズーム ズームボックスがクリックされたときに は , FindWindow からはウインドウがすて、 にズームしているかどうかによって , inZo omln あるいは inZoomOut が得られます。ズ ームボ、ツクスの処理もクローズボ、ツクスと ほば同じて、す。異なるのは TrackGoAway の代わりに TrackBox を使い , ズームそのも のは ZoomWindow て、実行するという点て、 す。 C MAGAZINE 120 1993 7 List 2 defaul t: GetItem(AppleMenuH, item, accName) : accNum = OpenDeskAcc(accName) : break; 46 : 47 : 48 : 49 : 50 : 53 : WindovPtr myWindov; 55 : void OpenMyWindov() if (myWindow ! ニ NULL) 58 : return; 59 : myWindov : GetNewWindov(WindovID, NUL し (void * ト 1 ) : 60 : if (myWindov = NULL) 62 : return; 63 : DisabIeItem(FiIeMenuH, 0penWindovI temID) ; 64 : EnableItem(FileHenuH, ロ osel temID) : 65 : 66 : } 68 : void CloseMyWindov() if (myWindov = NUL い return; DisposeWindov(myWindov) : 74 : myWindov : NULL; 75 : EnableItem(FileMenuH, 0penWindovItemID) : DisabIeItem(FileMenuH, CloseItemID) : 77 : 79 : 80 : void handleFileChoice(short item) switch (item) { 83 : case OpenWindovItemID: OpenMyWindow() : 84 : 85 : break; 86 : case CloseItemID: CloseMyWindov() : 87 : 88 : break; 89 : case QuitItemID: 90 : CloseMyWindow(); Done ニ true : 92 : break, 93 : 94 : } 95 : 96 : void handIeMenuChoice(Iong choice) 98 : int menu, item, 99 : if (choice ! = の { 100 : 101 : = HiWord(choice) ; menu 102 : : LoWord(choice) ; item svitch (menu) { 103 : 104 : case AppIeMenuID: 105 : handleAppIeChoice(item) ; 106 : break; 107 : case ・ FileMenuID: 108 : handleFileChoice(item) : 109 : break; 110 : case Ed i tMenuID: 111 : SystemEdit(item ー 1); 112 : break; 1 1 6 : 117 : voi d handIeMouseDown() 1 18 : 1 19 : 120 : WindowPtr 新 1 2 1 : short partCode; 122 : long choice; 1 2 3 : long size; 1 2 4 : Rect r; 125 : GrafPtr oldPort; 126 : partC0de ニ FindWindow(TheEvent. vhere, & の : svitch (partC0de) { 128 : 129 : case i nMenuBar. 130 : choice : MenuSelect(TheEvent. where) : 131 : handleMenuChoice(ch0ice) ; 132 : break; 133 : case inSysWindov. 134 : SystemCIick(&TheEvent, の : 135 : break; 136 : case i nGoAvay: if ( 響 ! ニ FrontWindov()) 137 : Hi liteMenu( の :

6. 月刊 C MAGAZINE 1993年7月号

98 & 00S / ゲームプログラミンク大作戦 lSt すべてのウインドウの始まりとなる DeskTop クラス と呼ぶことが多いのて、 , それにならって De skTop クラスを Window クラスから派生しま す (Fig. 3 ) 。 あるひとつのアプリケーションて、使用す るウインドウは , すべてこの DeskTop に所 属することになります。 DeskTop の行うこ とは , ひたすらマウスを監視して , マウス が押されたらそれがどのウインドウ上て、押 されたのかを発見し (findWindow()), その 見つけた Window にマウスが押されたことを 通知することて、す。通知といってもまだメ ッセージのたぐいは実装されていないのて、 , 各ウインドウの適切なマウス処理関数を呼 び出すだけて、す (List 7 , Fig. 4 ) 。 各 Window は insert( ) 関数を使用して , D eskT 叩の子ウインドウとして登録していき ます (List 8 ) 。こうすることによって , すべ てのウインドウが DeskTop 経由て、アクセス 可能になります。いいかえれば , 登録され ていないウインドウはマウスがその中て、押 ースて、は 1 倍ウインドウのみの問題なのて、 , 1 ドットずっ広げてやります。と , 同時に R されたかどうかの調査がされないのて、 , 何 この間題は捨ておくことにします。見栄え かを起こすきっかけを永遠に与えられず , ealWindow : : putPixeI (Pöint P) て、は右下 のいいウインドウに枠は必須なのて、 , その に 1 ドットシフトして描いてやる必要があり 存在しないも同然なのて、す。 うちにちゃんとやります。 ⅲ sert ( ) は引数て、与えられたウインドウ , ます。 なにやらめんどくさいことになってしま あるいはその派生クラスのオプジェクトを 子ウインドウリストの始め ( 終わりて、もよい ) いました。なせこのようなはめになったの か , 反省しつつ考察してみましよう。つま に追加して , 自分へのポインタを返します。 前回作成した Pattern 構造体にいくっかメ り , ウインドウの枠と描画領域の区別がな 自分へのポインタを返すことによって登録 ンバ関数を追加してクラスとして使用しま かったためにこのような手間が生じたのて、 したいウインドウを一度に連結処理するこ しよう。別に struct のままて、もかまいません す。て、も , 枠を別扱いにするほうがめんど とがて、きます。また 1 倍ウインドウ ( RealW が , class といっておいたほうがメンバ関数 くさいのて、はないて、しようか ? 今回のケ indow) は , 工ディットする幅より上下左右 Fig. 5 ZoomWindow は xl とノヾターンテータをアクセスする Fig. 4 目的のウインドウを見つける class DeskTop : public WindOW { public: DeskTop(const Rect& bounds) : Window (bounds) { } virtual void handleMouse(const P0int P) : void DeskTop: : handleMouse(const point P) { Window *wp; = NULL)) { / / どのウインドウにマウス if ((wp = findWindow(p)) ! = this & & ()p ! / / がいるのか取得ボタンが wp->handIeMouse(p) : / / 押された時の処理に制御 / / を移す し DeskTop に各 Window を登録する *deskTop = new DeskTop(Rect(0, 0 , 640 , 48 の ) : DeskTop RealWindow *rw = new ReaIWindow(Rect(200, 50 , 32 + 2 , 32 + 2 ) ) : ZoomWindow *zw = new ZoomWindow(Rect(300, 50 , 32 * zoom, 32 * zoom), zoom, &Pattern( ” test" , 0 , 32 , 32 ) ) ; deskTop ->insert(zw) ->insert(rw) ; Pattern クラス winl 上でマウスがクリックされると DeskTop → findWindow( ) •winl → findWindow( ) •win4 → findWindow( ) が検索されて win4 → handleMouse が起動される 1 倍ウインドウ W ・ inl Win3 Win4 Win2 Realwin Pattern パターンテータ ZoomWindow 98 & DOS / v ゲームプログラミング大作戦 73

7. 月刊 C MAGAZINE 1993年7月号

AIIWays テキストウインドウライプラリは、マルチベンタに対応 した C 言語スクリーン・ファンクション・ライプラリです。実行する に応じた高速・高なスクリーンを提供します。特にい N 対応アプリケーションの場合マルチベンダ文は非常に重要カつ困 難な問題です。 AIIWays を使用して作成したアプリケーションなら、 1 つの実行ファイルだけで多くに文できます。ーの実行 ファイルを用意する必要はありません。 ー依存部分を分離 AIIWays は、スクリーンやキーポードなどの機種依存部分をランタイ これらのモジュールは作 ム・エクステンションとして分難しました。 成した実行ファイルにリンクされるのてはなく、実行 1 にイ十ミッ クにロードされます。どの機種Ⅲのモジュールをロードするかは一竟 変数によって定しますから、作成した実行ファイルには機種依存部 分を含む必要はないのてす。 ・高速でコンパクトなランタイム・エクステンション 機秤依存部分をまとめたランタイム・エクステンションは、 l'C98()(), FMR 、 PS/.T) 、 J-3Ⅱ用、 AX.IX)S/V など、 i:. 要な国産バーソ十ルコ 、ンヒ。ューダⅢのものが殆ど提供されます。しかも、各機種の性能を最 大に活Ⅲてきるように高速・コンバクトに設制・されています。 ・多彩な入力処理を提供 人力フィールドのデータダイプ畆各種人力属性定、文字色、 カーソル表 4 リ鰍自利魃け出しこ F 樹制などがてきます、、ま た距形人力フィールドもサポートしています。フィールド内てのスク ロールもサポートしていますのて、実際の示フィールドより人きな データの人力・編集も可能 ( す。 ・ウインドウを提供 最人 64 個のオーヴァラップ・ウインドウが利Ⅲてきます。ウインドウ のポップアップ、移動、サイズ / カラー / タイトルの変史などが容易 てす。ウインドウ内てのスクロールも可能てす。 を用メニューの ポップアップ・メニュー、プルグウン・メニュー、メニュー・バー 階層メニューを容易に実現する関数を提供しています。階崩メニュー ては、特に表′置を定しなくてもサプメニューの適切位置表示が 自利的に行われます。 ・ランタイム・ライセンス・フリー 作成したアプリケーションは、機種依存部分のランタイム・エクステ ンションと共に 自山に配市・版売することがてきます。 〈価格》¥ 68.000 ※ソースコード付きヴァージョンもⅢ意されています ( 価格 : Y ー聞 . ( )) NPPC ライプラリ (Netwo 「 k Program-to-Program Communications) NPPC (Network Program-to-Program Communications) ライ プラリは、ネットワーク・アプリケーション開発をナる強力なサプ ルーチン・パッケージです ・旧 X と Net 0 方に N 円℃ライプラリには 2 つのヴァージョンがあります。 1 つは N ( Ⅳ e 社の IPX トランスポート・プロトコルに対応したヴァージョン ( 、もう 1 つは Net OS ヴァージョンてす。どちらのヴァージョンも同じ A 円 ( アプリケーション・プログラム・インタフェース ) を持っていますか ら、作成するアプリケーションはソース・レベル ( ん全なな換性があり ます。運川する、勲霓に応じた N 円℃ライプラリをリンクするだけてす。 ■言語インタフェース供 N 円℃ライプラリては、 C 言語およびアセンプリ語のインタフェー スが標 4 材是供されます。 IPX や NetBI()S のプログラミングについて 新たに・ 7 物する必要はありません。メッセージのキューイング、転送、 受取り、エラー時の再送、Ⅳ X や NetBI( ) ) 初期化および終「処理な どはすべて N 円℃によってコントロールされます .. メッセージは 日 FO モードあるいはイ定した優ん類にアプリケーションに渡すこと が可能 ( す。 ・アオ ) ケーション・モデル N 円℃ライプラリを使川して作成されたアプリケーションては、どの ステーションもクライアント、サーバあるいはその両方の機能を利川 てきます。非い JJY レい JJY レッセージ転送が、クライアント / サーパ・ モデルおよびヒ。ア・トウ・ピア・モデルてサポートされます。 ・フル・アセンプラ N 円℃ライプラリはⅡ灯 % アセンプラ ( 心されており、オフション てソースコードも提供しています .. なお、 N 円℃ライプラリを組み込 んだアプリケーションは約 KB のメモリを余分に必要とします ,. ・ランタイム・ライセンス・フリー N 円℃ライプラリを組み込んだアプリケーションは、追加費川を支払 うことなく、自山に販売・配布することがて・きます .. 〈価格〉 NPPC forlPX ¥ 6 圧 000 NPPC fo 「旧 X / ソースコード付き¥ 120.000 NPPC fo 「 NetBlOS ¥ 68.000 NPPC fO 「 Net 日旧 S / ソースコード付き¥ 120.000 ※田 M - P ( 数と NEC ー℃ 980 ( はそれそ・れ別嬲てす。いずれも MS-D()S v3.1 以 l•. が必要てす。 ※田 M P ( は OS レベルて田 M ヨ℃とな換性のあるマシンて・あ れば使川可能 ( す。 ! 均な機種についてはお間い合わせください ・お間い合わせは ージーテック List 4 ) いいにリ ( 」に ( にい ~ にに。ににいリー 58 : 59 : 朝 60 : 63 : 64 : 第 65 : 66 : 68 : 70 : 72 : 74 : 76 : 77 : 78 : 80 : 82 : 83 : 85 : 86 : 87 : 88 : 89 : 90 : 92 : 93 : 94 : 95 : 96 : } 第 98 : #pragma argsused int nShov) 99 : int PASCAL WinMain(HANDLE hlnst, HANDLE hPrev, LPSTR IPCmd, 100 : { static char szAppName[] 101 : つ 102 : HWND hwnd; 103 : MSG msg, 104 : WNDCLASS vndclass; 105 : if (!hPrev) { 106 . wndclass. style : CS-HREDRAW ー CS-VREDRAW; 107 : WndProc; 108 : wndcIass.IpfnWndProc vndclass. cbClsExtra ニ 0 : 109 : vndclass. cbWndExtra ・ニ 0 : vndclass. hlnstance = hlnst; vndclass. hlcon ニ LoadIcon(NULL, IDI-APPLICATION) : vndclass. hCursor = LoadCursor(NUL し IDC-ARROW) : vndclass. hbrBackground : GetStock0bject(WHlTE-BRUSH) : ニ NULL; wndclass.lpszMenuName szAppName; 1 16 : vndcIass.lpszClassName RegisterClass(&vndcIass) : 1 18 : い 1 19 : 120 : 121 : 122 : 1 2 4 : 125 : 126 : 128 : 129 : 第 130 : 131 : 132 : } static char し uf [ 80 ] : PAINTSTRUCT ps; RECT rect; sv•itch (msg) { case WM_CREATE: / * テキスト画面の内容を設定し、 V i d eo I n i t を呼び出す * / SetupScreen() : vsprintf(buf, ” Press left button t0 svitch DOS screen(result ニ %d)" V i de n i t ( ) ) : return 0 : case WM_PAINT: / * メッセージを表示 * / BeginPaint(hwnd, &ps) : GetClientRect(hwnd, &rect); DravText(ps. hdc, buf, ー 1 , &rect, DT-SINGLELINEIDT_CENTERIDT_VCENTER) ; EndPaint(hvnd, &ps) : return 0 : case WM_LBUTTONDOWN : / * 左ボタンが押されたらテキスト画面に移行 * / VideoDebuggerScreen(); SetCapture(hwnd) ; return 0 ; case WM_LBUTTONUP: / * 左ボタンが離されたら Windovs の画面に復帰 * / VideoWindowsScreen() : ReIeaseCapture() ; return 0 : case WM_DESTROY: / * VideoDone の呼び出し * / VideoDone(); PostQuitMessage(0) ; return 0 : return DefWindowProc(hwnd, msg, wParam, IParam) : ” VideoSwitch ” hwnd ” The Video Swi tch Program CreateWindov(szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NUL し NULL, hlnst, N 乢い ; Sh0 石 ndo れ h Ⅶ山 nShov) : UpdateWindow(hvnd) : vhile (GetMessage(&msg, NUL し 0 , TransIateMessage(&msg) : DispatchMessage(&msg) : return msg. vParam; く資料請求番号 139 > PC 98 田 #}TDVIDEO の作り方 139

8. 月刊 C MAGAZINE 1993年7月号

ニ : ロ 学 五ロ ウインドウとを比較します。 アクテイプて、ないウインドウて、マウスが owlcon はサイズボックスとスクロー ノレノヾ クリックされたときは SelectWindow を使っ の箱を描く関数て、す。 クローズ てそのウインドウをアクテイプにしなけれ イベントの処理 ばなりません。そのウインドウがアクティ クローズボックスて、マウスがクリックさ プウインドウかどうかを調べるためには Fr れたときは FindWindow は定数 inGoAway ontWindow を使います。 FrontWindow は現 ウインドウに関連するイベントはいくつ を返します。このときは TrackGoAway を 在いちばん手前に表示されているウインド かありますが , 今回はアップデートイベン 呼び出してマウスポタンが離されるまて、マ ウを返します。これがアクテイプウインド トとマウスダウンイベントの処理について ウスの動きを追跡します。 TrackGoAway ウて、すから , これと FindWindow によって 説明します ( 実はこのほかにアクティベート はボ、タンがクローズボ、ツクスの中て、離され 得られたマウスダウンイベントが発生した イベントがありますが , これはまた別の機 会に説明します ) 。 アップテートイベントの処理 1 : case updateEvt: アップデートイベント GetPort(&01dPort) ; 2 : SetPort(myWindow) ; 3 : BeginUpdate(myWindow) : 4 : 5 : / * 描画処理 * / アップデートイベントはウインドウマネ 6 : 7 : EndUpdate(myWindow) : 8 : ージャが生成するイベントて、 , ウインドウ SetPort(oldPort); 9 : の再描画が必要なことを示します。このと きのイベントレコードの message フィールド には再描画するべきウインドウへのポイン タが格納されています。 アップデートイベントに対応しての描画 て、は , イベントの処理中て、あるという事実 を T001BOX に知らせるために , 描画の前後 を BeginUpdate と EndUpdate て、くくる必要 があります。 以上をまとめると , アップデートイベン トの処理は List 1 のようになります。 マウスダウンイベント ウインドウそのものが処理をするのはマ ウスに関するイベントて、す。ウインドウの どこかがクリックあるいはドラッグされた ときには , FindWindow て、どこて、マウスが クリックされたのかを調べます (Fig. 5 ) 。 アクテイプなウインドウ アプリケーションがいくつもウインドウ を開いている場合 , 現在アクテイプになっ ているのはいちばん手前にありタイトルバ ーやスクロールバーがきちんと表示されて いるウインドウて、す。そのほかのアクティ プて、ないウインドウはタイトルバーなどが 白く表示されています。 List 1 Fig . 5 FindWindow short FindWindow(Point thePoint, WindowPtr whichWin) ; マウスがクリックされたポイント thePoint マウスがクリックされたウインドウレコードのアドレス whichWin マウスがクリックされた部分のコード リターン SampIe3. c 1 : #include く Types. h> 2 : #include く 0SUtils. h> 3 : #i nclude く Menus. h> 4 : $include く Dialogs. h> 5 : $include く Contr01s. h> 6 : #i nclude く Desk. h> 7 : #include く TooIUtiIs. h> 8 : #include く Fonts. h> 9 : #include く 0SEvents. h> 11 : #define MenuBarID 128 13 : #define AppleMenuID 128 14 : #define AboutItemID 1 15 : #define AboutAlertID 128 17 : $define FileMenuID 129 18 : #define OpenWindowItemID 1 1 9 : #def i ne ロ ose e 耐 D 2 20 : #define QuitItemID 4 22 : #define EditMenuID 130 23 : $define UndoItemID 1 24 : #define CutItemID 3 25 : $define CopyItemID 4 26 : #define PasteItemID 5 27 : 28 : #define WindowID 128 30 : $define WNE_TRAP 0X60 31 : $defi ne UNIMPL-TRAP 0x9F 32 : 33 : Boolean Done; 34 : MenuHandIe AppleMenuH, F ileMenuH ; 35 : EventRecord TheEvent ; 37 : void handIeAppIeCh0ice(short item) Str255 accName; int accNum, svitch (item) { 43 : case AboutItemID: NoteAIert(AboutAlertID, NUL い : break; List 2 C 言語雑学講座 119

9. 月刊 C MAGAZINE 1993年7月号

60 て、すから , 求める解も 2 , 540 , 160 以下にな っているはずて、す。 以上の考察を元に , パズルを解くための もっとも基本的なアルゴリズムに基づくプ ログラムを作成しました。 List 6 がそれて、 す。 List 6 の要点は関数 factsum て、す。この 関数により階乗和を計算し , それが元の整 数と等しければ求める解になっていること になります。 List 6 は単純て、わかりやすいのて、すが , 探 索を終了するのに約 24 分 ( 386SL / 25MHz ) か かります。単に答えを求めるだけて、あれば この程度の速度て、も十分て、すが , プログラ マの心情としてはもっと高速な解法が欲し くなりますね。次の節て、は , このパズルの 表計算法に基づく高速化について検討する ことにします。 List 6 } ⅶⅱ e (d > の : return sum, -4 ・ LO ー - 8 00 0 1 よワ 0 っ 0 ・ - 、リ叮ー っ 0 90 ワ朝ワ 0 っ 0 っ 0 00 っ 0 ワ 0 り 0 り 00 00 00 long i, maxnum; ニ fact(9) * 7 : maxnum for(i ー 0 : i く = maxnum; i 十十 ) if(factsum(i) printf(" 解 = %ld%n ” return 0 : / * 探索の上限を求める * / / * 階乗和と自身が等しければ * / / * 解として表示 * / Fig. 4 マクロなムダ ( 1 ) 上位桁はほとんど 末尾桁は頻繁に 変化しない 変化する 1 0 0 0 0 0 0 1 1 0 0 0 0 0 2 1 0 0 0 0 0 3 変化の少ない上位桁について , いちいち階乗和を 計算するのはムダである。 表計算法によって高速化が行えるのは , 要は同じ計算を何回も行っている場合て、す。 List 6 について , 「同じ計算」を繰り返してい る部分がないか , 検討してみましよう。 まず真っ先に目につくのは , 階乗の計算 を何度もやり直していることて、す。階乗の 計算は 0 ~ 9 の 10 通りしか行わないのて、すか ら , あらかじめ計算して一覧表にしておけ ば , ムダな計算を避けることがて、きます。 これは典型的な「表計算法」の応用といえま す。 こて、調べたのは一桁の計算の部分て、 , いわばミクロな部分についての改良て、すが , List 6 をよく調べてみるともっと広い範囲て、 冗長な計算を繰り返しているのて、す。これ はいわばマクロなムダといえます。一般的 な経験則として , ミクロな改良よりもマク ロな改良のほうが , はるかに効果が大きい ことが知られています。以下 , これについ て検討します。 まず Fig. 4 をご覧ください。この図は探索 の過程を図示したものて、すが , ご覧のよう , 上位桁はほとんど変化していない 高速解法の検討 表計算法に基づく数学パズルのプログラム list7. c List 7 1 : / * f ⅱ e ニ 2 : 3 : # i nc lude 4 : # i nc lude 6 : #define TABLE-SIZE 1000 7 : 8 : long f-tabIe[10]; 9 : long factsum-tabIeCTABLE-SIZE] ; / * 階乗和の一覧表 * / 11 : / * 階乗を求める関数 1 3 : long fact(int n) return 1 : 20 : 21 : / * f ー ta い e を初期化する。 * f ー ta い e は一桁の階乗の表である。 22 : 23 : 24 : void init-f-table(void) 25 : { 26 : 27 : 28 : 29 : } 30 : 31 : / * factsum-table を初期化する。 * factsum-table は " 000 " から " 999 " までの階乗和の表である。 32 : 33 : 34 : void init-factsum-table(void) く stdio. h> <l i m i ts. h 〉 e ー se return fact(n for (i = 0 : i く 10 : i + + ) f-table[i] : fact(i); 90 C MAGAZINE 1993 7

10. 月刊 C MAGAZINE 1993年7月号

ln 川 a ⅱ行匪 III 町ⅱ計 Ma れ門 ボーランド BorIand C 十十 Turbo C 十十 fo 「 Windows 基本クラスのⅲ nstance を呼び出してからメッセージボックスを表示 List 1 Q ObjectWindows でアプリケー ションを作成していますが , アプ リケーションを起動するときに 度だけメッセージを表示するのは どうすればよいでしようか A TApplication の派生クラスて、 lnitlnstance メンノヾ関数をオーバラ イドし , 基本クラスの lnitlnstanc e を呼び出してからメッセージボ、ツ クスなどを表示します (List 1 ) 。メ インウインドウが表示される前に メッセージを表示したい場合はメ インウインドウに割り当てるウィ ンドウクラスて、 SetupWindow メン バ関数をオーバライドします (List Q C 十十のクラスを使って Wind ows の DLL を作成するときの注意 点を教えてください。 A c 十十のクラスを Windows の DLL に定義し , 実行ファイル (. EX E) て、そのクラスを継承するような 場合は , C て、 DLL を作成する場合に 比べて制約や注意すべき点がいろ いろあります。具体的な注意点を 以下に示しますのて、 , 参考にして ください。 DLL の作成や利用に関 しては , マニュアルをご覧くださ 1 ) ラージモデルで開発する DLL と . EXE は , それぞれ固有の データセグメントを持ちます。 れは , 両者て、共有する可能性のあ るものは , far て、宣言する必要があ ることを示しています。たとえば , DLL と . EXE のどちらて、もオプジェ クトを生成することがて、きますか ら , this•'* インタはどちらのデータ も正しくアクセスて、きるよう far,-\ インタて、なければなりません。同 様にメンバ関数も far 関数て、なけれ ばなりません。 データポインタとコードボイン タが両方とも far ポインタて、なけれ ばならないため , ラージモデルを 使う必要があるのて、す。なお , W indows アプリケーションて、は , hu ge モデルは使えません。 2 ) クラスの宣言 クラスが仮想関数を持っ場合 , そのクラスは仮想関数テープルへ のポインタを持ちます。通常 , 仮 想関数テープルは , nearztk インタ によって示されています。これは , ラージモデルて、も同じて、 , ラージ モデルて、は near 領域にある仮想関 数テープルの要素が far 関数へのポ インタを示しています。 1 ) と同じ理由により , 仮想関数 テープルも DLL と . EXE て、共有す るためには far ポインタによって示 される必要があります。このため には , クラスの宣言を huge か _exp ort のいずれかて、宣言するかー Vf オ プションを使います。 huge は , ク ラスの仮想関数テープルをデータ セグメントて、なくコードセグメン トに配置し far ポインタによってア クセスするようにします。 -Vf オプ ションは , コマンドラインからこ れを指定するオプションて、す。 e xport は huge と似ていますが , クラ ス内のメンバ関数をすべて工クス ポート関数にします。 DLL と . EXE て、クラスを共有す るときは , . EXE から DLL 内のメン バ関数を呼び出すこともあります から , DLL 内て、のクラスは expor t を使って宣言しなければなりませ 1 : class TMYApp: public TApplication { 3 : pub ⅱ c : void lnitlnstance(void) TAppIication::InitInstance(); MessageBox(Mai nWindow->HWi ndov, 9 : 8 : 7 : 6 : 5 : 4 : 2 : 10 : - } , ” He Ⅱ 0 ” . MB_OK); 6 : 1 : ” Startup Message , メインウインドウが表示される前にメッセージを表示 List 2 1 : class TMainWindow: public TWindow { pub ⅱ c : void SetupWindow(void) { TWindow: :SetupWindow() ; MessageBox(HWindow, ” Startup Message 7 : 5 : 4 : 3 : 2 : [common. h] List 3 DLL と . EXE てクラスを共有する例 ” He Ⅱ 0 ” MB_OK) : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10 : 1 : 3 : 5 : 1 : 3 : 5 : 6 : #if defined(--DLL-_) #define -EXPORT -export #else #define _EXPORT huge #endi f class -EXPORT A { pub ⅱ c : virtual void func(); int num; Cdll-prog. cpp] #include ” common. h" 2 : / / 基本クラス内のメンパ関数の実装 void A::func(void) puts( ” A::func"); [exe-prog. cpp] #include ” common. h ” 2 : / / DLL で実装されたクラスの利用 VOid global-func() A object; object. func(); 162 C MAGAZINE 1993 7