筑波大学 理工情報生命学術院 システム情報工学研究群 情報理工学位プログラム 2017年8月実施 基礎科目 問題IV
Author
祭音Myyura
Description
整数と表 1 に示した演算子で構成された数式を木構造で表現する. 図 1 に,そのような数式を表す木構造の例を示す. 木構造を深さ優先で走査する方法として,先順 (pre-order, 前順),中順 (in-order, 間順),後順 (post-order) がある. 図 1(A) に示した木構造を中順で走査すると,次のような中置記法による数式が得られる. ただし,この数式は計算の順序を示すために括弧を含んでいる.
((10 + 20) - 30)
数式を表す木構造を後順で走査すると,後置記法 (逆ポーランド記法) による数式が得られる. 図1(A) に示した木構造を後順で走査すると,次のような数式が得られる. ただし,この数式で要素の区切りは空白である.
10 20 + 30 -
表 1: 整数を扱う演算子
演算子 | 説明 |
---|---|
+ | 2つの引数を取り,それらの値を加え,その結果を返す. |
- | 2つの引数を取り,最初の値から 2 番目の値を引き,その結果を返す. |
== | 2つの引数を取り,それらの値を比較する.それらが等しい場合,真 (0 ではない値) を返す.そうではない場合,偽 (0) を返す. |
! | 1つの引数を取り,それが真の場合,偽を返す.そうではない場合,真を返す. |
? | 3つの引数を取り,最初の値をチェックする.最初の値が真の場合,2 番目の値を返す.そうではない場合,3 番目の値を返す. |
(A) (B) (C)
(-) (+) (?)
/ \ / \ / | \
(+) (30) (10) (+) (==) (30) (40)
/ \ / \ / \
(10) (20) (20) (+) (10) (20)
/ \
(30) (40)
図 1: 数式を表す木構造の例
次の小問に答えなさい.ただし,以下の図に示されたプログラムは,C 言語で記述されている.
(1) 図1 (B),および,図1 (C) に示した木構造を,後置記法で表記しなさい.
(2) 次の 2つの数式は,後置記法で表記された木構造を表している.これらの木構造を,図 1 と同じ形式で図示しなさい.
- (D) 1 2 + 3 4 - +
- (E) 1 2 == ! 0 1 ?
(3) 図 2 に示した関数 calc() は,後置記法で表記された数式をスタックを用いて計算する. この関数は,文字列の配列で表現された数式を引数として取る.配列の各要素は,整数を表す文字列,または,演算子を表す文字列である. 配列は,NULL ポインタで終端されている. この関数は,計算の途中結果をスタックに保存する. 関数 calc() が利用しているスタックは, 表 2 に示した関数で操作される. また,関数 calc() は,表 3 に示した関数を利用している. ただし,関数 calc() はエラー処理に問題がある. 図 3 に示した関数 main() が実行された時,画面にどのような出力がなされるかを示しなさい.
表 2: スタックを操作する関数
関数 | 説明 |
---|---|
void init_stack() | スタックを空にし,大域変数 error を 0 に初期化する. |
void push(int x) | スタックに整数 x を積む. |
int pop() | スタックが空の場合には,大域変数 error に 1 をセットし,0 を返す.このエラーは,スタック・アンダーフローとして知られている.そうではない場合,スタックから整数を取り出し,それを返す. |
int free_stack() | スタックが空の場合,真を返す.そうではない場合,スタックの全ての要素を解放し,偽を返す. |
表 3: 関数 calc() が文字,文字列,および,整数を操作するために利用する関数
関数 | 説明 |
---|---|
strcmp() | 2 つの文字列を引数に取り,それらが等しい場合,0 を返す.そうではない場合,0 以外の値を返す. |
isdigit() | 文字を引数に取り,それが数字 (’0’ から’9’ の間) である場合に真,そうではない場合に偽を返す. |
atoi() | 数字から構成される文字列を引数に取り,その文字列を 10 進数とみなし整数に変換し,その整数を返す. |
#define NULL ((void *)0)
#define OK 1
#define ERROR 0
extern int error;
int calc(char *exp[], int *xp) {
init_stack();
for (int i = 0; exp[i] != NULL; i++) {
if (isdigit(exp[i][0]))
do_num(exp[i]);
else if (strcmp(exp[i], "+") == 0)
do_add();
else if (strcmp(exp[i], "-") == 0)
do_sub();
else if (strcmp(exp[i], "==") == 0)
do_eq();
else if (strcmp(exp[i], "!") == 0)
do_not();
else if (strcmp(exp[i], "?") == 0)
do_cond();
else {
free_stack();
return (ERROR);
}
}
*xp = pop();
free_stack();
if (error) /*(X)*/
return (ERROR);
else
return (OK);
}
void do_num(char *s) {
int a;
a = atoi(s);
push(a);
}
void do_add() {
int a, b;
b = pop();
a = pop();
push(a + b);
}
void do_eq() {
int a, b;
b = pop();
a = pop();
push(a == b);
}
void do_not() {
int a;
a = pop();
push(!a);
}
図 2: 後置記法の数式を計算する関数 calc()
int main() {
int x = 0;
char *exp[] = {"30", "50", "+", NULL};
if (calc(exp, &x) == OK)
printf("OK. The result is %d.\n", x);
else
printf("Error.\n");
return (0);
}
図 3: 関数 calc() の利用例
(4) 図 4 の空欄を埋めて,関数 do_sub(),および,do_cond() を完成させなさい.
void do_sub() {
int a, b;
[ 空欄 (F) ];
[ 空欄 (G) ];
push( [ 空欄 (H) ] );
}
void do_cond() {
int a, b, c;
[ 空欄 (I) ];
b = pop();
[ 空欄 (J) ];
if (a)
[ 空欄 (K) ];
else
[ 空欄 (L) ];
}
図 4: 関数 do sub() と do cond()
(5) 図 2 の関数 calc() は,実行中にスタック・アンダーフローのエラーが生じ,ERROR を返す時がある. そのような入力の例を1つ示しなさい.
(6) 図 2 の関数 calc() は,「/*(X)*/」と印を付けた所で大域変数 error が真の場合,ERROR を返す. そうではない場合,この関数は,OK を返す. しかし,入力の数式がある種のエラーを含んでいる時,そのエラーを検出できず,OK を返すことがある. そのような入力の例を1つ示しなさい.
(7) 小問 (6) の問題を解決するために,関数 calc() を修正する.関数 calc() への修正方法の概略を示しなさい.
(8) 表 2 に示した関数を,リストを用いて実装する. 図 5 の空欄を埋めて,関数 push(),および,pop() を完成させなさい.ただし,関数 malloc() は常に成功するものとする.
#define NULL ((void *)0)
struct element {
struct element *next;
int data;
};
struct element *sp = NULL;
int error = 0;
void push(int x) {
struct element *n;
n = malloc(sizeof(struct element));
[ 空欄 (M) ];
[ 空欄 (N) ] = x;
[ 空欄 (O) ];
}
int free_stack() {
// コード省略
}
int pop() {
int x;
struct element *f;
if ([ 空欄 (P) ]) {
error = 1;
return (0);
}
[ 空欄 (Q) ];
f = sp;
[ 空欄 (R) ];
free(f);
return (x);
}
void init_stack() {
free_stack();
sp = NULL;
error = 0;
}
Kai
(1)
- 図 1(B): 10 20 30 40 + + +
- 図 1(C): 10 20 == 30 40 ?
(2)
(D) (E)
(+) (?)
/ \ / | \
(+) (-) (!) (0) (1)
/ \ / \ /
(1) (2) (3) (4) (==)
/ \
(1) (2)
(3)
OK. The result is 80.
(4)
- [ 空欄 (F) ]: a = pop()
- [ 空欄 (G) ]: b = pop()
- [ 空欄 (H) ]: b - a
- [ 空欄 (I) ]: c = pop()
- [ 空欄 (J) ]: a = pop()
- [ 空欄 (K) ]: push(b)
- [ 空欄 (L) ]: push(
)
(5)
char *exp[] = {"30", "+", NULL};
(6)
char *exp[] = {"30", "50", "40", "+", NULL};
(7)
int calc(char *exp[], int *xp) {
// コード省略
*xp = pop();
int flag = free_stack();
if (error || (!flag)) /*(X)*/
return (ERROR);
else
return (OK);
}
(8)
- [ 空欄 (M) ]: n->next = sp
- [ 空欄 (N) ]: n->data
- [ 空欄 (O) ]: sp = n
- [ 空欄 (P) ]: sp == NULL
- [ 空欄 (Q) ]: x = sp->data
- [ 空欄 (R) ]: sp = sp->next