注
以下のようなプログラムを作成せよ.
- キーボードから正整数を順に入力し,
入力された値をプログラム内に記憶させる.
-
値取りだしコマンドの入力によって,
記憶に残っているデータの中で最も最近に入力したデータを画面に表示する.
- 値取りだしコマンドによって表示されたデータは,
プログラム内の記憶から削除する.
- スタックと呼ばれる計算機で最も重要なデータ構造の振る舞いを理解する.
- 配列の利用方法を理解する.
- ./a.out でプログラム開始.
- 標準入力から quitと入力することでプログラム終了.
- 数値を入力することで,データが追加される.
ただし,保持できるデータ数には上限があることとし,
既にデータを追加できない場合は,その旨が表示され,
データの追加は行われない.
- 値取りだしコマンド pop で最も最近入力されたデータ1つが
表示され,その数値はプログラム内の記憶から削除される.
もし,データが全くない場合は,その旨が表示される.
- 表示コマンド show で現在保持しているデータを入力順に表示する.
- 1行目で,プログラムを実行している.
- 2〜7行目で6個のデータを入力し,
- 8行目でコマンドshowによって入力内容を確認している.
結果は9行目に表示されている.
- 10行目でpopコマンドで値を取り出すと,一番最近に入力したデータ,
3が表示される(11行目).
- 14行目でpopコマンドで値を取り出すと,最後から2番目に入力された
7が表示される(15行目).
- 定義されていないコマンドを入力すると,その旨が表示される
(19行目).
- すでにデータが空の状態でpopすると(28, 30行目),
データがない旨が表示される(29, 31行目).
- quitでプログラムは終了する(39行目).
[ 1] % ./a.out
[ 2] 3
[ 3] 5
[ 4] 1
[ 5] 2
[ 6] 7
[ 7] 3
[ 8] show
[ 9] [ 3 5 1 2 7 3 ]
[10] pop
[11] 3
[12] show
[13] [ 3 5 1 2 7 ]
[14] pop
[15] 7
[16] show
[17] [ 3 5 1 2 ]
[18] aa
[19] Unknown command "aa"
[20] pop
[21] 2
[22] pop
[23] 1
[24] pop
[25] 5
[26] pop
[27] 3
[28] pop
[29] Stack UnderFull, could not pop.
[30] pop
[31] Stack UnderFull, could not pop.
[32] 3
[33] show
[34] [ 3 ]
[35] pop
[36] 3
[37] pop
[38] Stack UnderFull, could not pop.
[39] quit
- スタックはLast-In First-Outの振る舞いを持つデータ構造である
[1, 10ページ].
すなわち,最も最近に入力したデータから,値を取り出すことができるため,
最初に入力したデータは最も最後に取り出すことができる.
- これは,丁度,本などを机に積み上げた場合に似ている.
- スタックの振る舞いの一端を図示すると以下のようになる.
図1: スタック操作の例
図1からわかるように,
図中で入力されたデータを全て取り出した後でないと,
最初から入っているデータ10を取り出すことができない.
- スタック・データ構造は,
関数の入れ子呼び出しの実現や,
数式の計算などソフトウェアの世界で幅広く利用されている.
例えば逆ポーランド記法[2]の計算などに利用される.
定数・定義
- MAX_STACK
- MAX_INPUT
- 値: 既に定義されていなければ512
- 入力されるコマンド長の上限を指定.システム側で指定されている場合有り.
- CMD_QUIT
- CMD_POP
- CMD_SHOW
- 値: "show"
- 現在入力されている値の表示コマンドの文字列.
共有変数
- int stack[MAX_STACK]
- int stack_ptr
もっとも最近に入力された値を保持する配列stackの添え字を保持.
初期値は(-1)
値は 0 <= stack_ptr < MAX_STACK
関数の仕様
- void show()
説明 | 現在保持している数値を画面に表示 |
参照する共有変数 | stack, stack_ptr |
入出力 | 保持している数値を画面に表示 |
- int pop(int* r)
説明 | 最も最近入力されたデータを(*r)に返す. |
引数 | r: 値を返す変数のアドレス. |
返り値 | 返す値があれば1, なければ0. |
参照・変更する共有変数 | stack, stack_ptr |
- int push(int r)
説明 | データrをプログラムに記憶させる. |
引数 | r: 記憶させる値. |
返り値 | 記憶できれば1, できなければ0. |
参照・変更する共有変数 | stack, stack_ptr |
- int digit_string(char* s)
説明 | 数値のみで構成される文字列か否かを判定する. |
引数 | s: 判定する文字配列の先頭アドレス.NULLは受け入れられない. |
返り値 | 数値のみなら1, そうでなければ0. 長さ0の文字列も0を返す |
- main()
説明 | ユーザーからの入力を受け取り,
それに従い他の関数を呼び出し,場合によっては結果や警告を表示する.
|
変更する共有変数 | stack_ptr: (-1)に初期化する.(プログラム62行目) |
入出力 |
標準入力からのユーザー入力.
標準出力へ関数返り値や警告などを出力.
|
呼び出すユーザー定義関数 |
show, pop,
push, digit_string.
|
ユーザー定義関数から参照されるローカル変数 |
char ibuf[MAX_INPUT]: プログラム61行目.digit_stringに引数として与えられる,
char ibuf[MAX_INPUT]: 整数値に変換され pushに引数として与えられる.
|
ユーザー定義関数から変更されるローカル変数 |
int r: プログラム74行目.popに&rが渡すことでpop内で変更される.
|
- void show():
- i=0〜stack_ptr まで以下の処理を繰り返す.
- stack[i]を画面表示.
- int pop(int* r):
- もしstackに値が保持されていれば,(プログラム33行参照)
- (*r)にstack[stack_ptr]の値を代入.
- stack_ptrを1減らす.
- 1を返す.
- そうでなければ0を返す.
- int push(int r):
- もしstackに値を追加できるのなら,(プログラム43行参照)
- stack_ptrを1増やす.
- stack[stack_ptr]にrの値を代入.
- 1を返す.
- そうでなければ0を返す.
- int digit_string(char* s):
- もし,sが空文字列なら0を返す.
- sを先頭から順に最後まで走査する.
- もし *s が数字でなければ0を返す.
- 1を返す.
- main():
- stack_ptrを(-1)に初期化.
- 標準入力からのデータを char ibuf[] に保存し,
入力データが無くなるまで以下を繰り返す.
- ibufが CMD_QUITと等しければ,繰り返しを抜ける.
- ibufが CMD_SHOWと等しければ,show() を呼び,繰り返し先頭に戻る.
- ibufが CMD_POPと等しければ,
- ローカル変数 int r を確保する.
- pop(&r)を実行する.
- popの返り値が0なら警告を表示して,繰り返し先頭に戻る.
- popの返り値が1ならrを画面に表示して,繰り返し先頭に戻る.
- ibufが 全て数字で出来た文字列ならば,(digit_stringで判定)
- ibufを整数に変換し,push関数に渡す.(プログラム83行参照)
- 返り値が0なら警告を表示.
- 繰り返し先頭に戻る.
- 該当するコマンドがなく,数値でもなければ警告を表示し,
繰り返しの先頭に戻る.
スタック構造を操作する関数を pop, push, show に限定したことで,
スタックに対する操作をシンプルにまとめることができた.
実際のスタックデータはグローバル変数として実装したが,
これをスタックを操作する関数からのみ参照できる形にすれば,
プログラムの保守性は向上すると思われる.
その理由は,現時点ではスタックに関係ない他の関数が自由に
スタックデータの実体(stack[]およびstack_ptr)を操作することができ,
整合性のないデータに改竄してしまう恐れがあるからである.
具体的にスタックデータを特定の関数からのみ参照できるようにするには,
例えば,スタックを操作する関数を1つのソースファイルに集め,
スタックデータ(配列)をstatic宣言する方法などがある.
これによって,
そのファイルに属さない関数からはスタックデータを参照できなくなる.
ユーザーとの対話をmain関数に集約したため,
ユーザーインタフェース様式の変更に柔軟に対応できると思われる.
例えば,現時点では標準入出力を利用してユーザーとのインタラクションを行うが,
スタック操作関数の変更なしに,
GUIを用いたインタラクションに置きかえることが可能である.
しかし,
唯一,show関数のみが直接に標準出力への出力を行っているため,
この部分は修正すべきである.例えば,
int show(int s[], int n)
説明 | 現在保持している数値を返す |
参照する共有変数 | stack, stack_ptr |
引数 | s: 値を返す配列の先頭アドレス, n: 配列の要素数 |
返り値 | 0: 返す値がない.-1: 返す値のほうがnより大きい, それ以外: 現在保持しているデータ数 |
などとすれば,インタフェースの変更へも柔軟に対処できるようになる.
-
中田育男.
コンパイラ.
新コンピュータサイエンス講座. オーム社, Jun. 1995.
ISBN 4-274-13013-4, 193 pages, 値段 3200円+税,
-
http://msdos.cs.shinshu-u.ac.jp/mc/lesson/rpolish.html
(2000年12月現在)
[ 1] /* $xId: main.c,v 1.3 2000-12-17 16:33:23+09 kaiya Exp kaiya $ */
[ 2]
[ 3] #include <stdio.h>
[ 4]
[ 5] #define MAX_STACK 256
[ 6] #ifndef MAX_INPUT
[ 7] #define MAX_INPUT 512 /* max size of a char input buffer */
[ 8] #endif
[ 9]
[10] #define CMD_QUIT "quit"
[11] #define CMD_POP "pop"
[12] #define CMD_SHOW "show"
[13]
[14] /* prototype dec's */
[15]
[16] void show(void);
[17] int pop(int*);
[18] int push(int);
[19] int digit_string(char*);
[20]
[21] int stack[MAX_STACK];
[22] int stack_ptr;
[23]
[24] void show(){
[25] int i;
[26] printf("[ ");
[27] for(i=0; i<stack_ptr+1; i++)
[28] printf("%d ", stack[i]);
[29] printf("]\n");
[30] }
[31]
[32] int pop(int* r){
[33] if(0 <= stack_ptr && stack_ptr < MAX_STACK){
[34] (*r)=stack[stack_ptr];
[35] stack_ptr--;
[36] return 1;
[37] }else{ /* underflow */
[38] return 0;
[39] }
[40] }
[41]
[42] int push(int r){
[43] if(stack_ptr+1<MAX_STACK){
[44] stack_ptr++;
[45] stack[stack_ptr]=r;
[46] return 1;
[47] }else{
[48] return 0;
[49] }
[50] }
[51]
[52] int digit_string(char* s){
[53] if(!*s)
[54] return 0; /* length zero string */
[55] for(; *s; s++)
[56] if(!isdigit((int)*s)) return 0;
[57] return 1;
[58] }
[59]
[60] main(){
[61] char ibuf[MAX_INPUT];
[62] stack_ptr=(-1);
[63]
[64] while(gets(ibuf)!=NULL){
[65] if(!strcmp(ibuf, CMD_QUIT))
[66] break;
[67]
[68] if(!strcmp(ibuf, CMD_SHOW)){
[69] show();
[70] continue;
[71] }
[72]
[73] if(!strcmp(ibuf, CMD_POP)){
[74] int r;
[75] if(!pop(&r))
[76] printf("Stack UnderFull, could not pop.\n");
[77] else
[78] printf("%d\n", r);
[79] continue;
[80] }
[81]
[82] if(digit_string(ibuf)){
[83] if(!push(atoi(ibuf)))
[84] printf("Stack Full, could not push.\n");
[85] continue;
[86] }
[87]
[88] printf("Unknown command \"%s\"\n", ibuf);
[89] }
[90] exit(0);
[91] }
$Id: index.html,v 1.10 2000-12-23 19:06:46+09 kaiya Exp kaiya $