━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 課題2 (数式データの操作) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●準備 次のファイルを,ディレクトリ ~/AdvProg/T6/ に保存しておく. exp.h 算術式の処理のためのヘッダ exp.c 算術式の操作 ex2.c 課題2の動作確認用 Makefile 自動コンパイル用 これらを課題1のファイル (tree.h, tree.c) と併用し,Makefile は上書きする. 予習時に上記のソースファイルと以下の課題を熟読し,疑問点を明らかにしておく. 文字の種類による判定に,isalnum() などの ctype.h で宣言された標準ライブラリの 関数群を使うので,あらかじめ使いこなせるようにしておく. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ●課題一覧 基本課題 2a 数式の読み込みと表示 2b 数式の値の計算 2c 数式データの基本操作 発展課題 2d 進んだ数式操作 2e 値の割り当ての変更 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ●基本課題 2a (数式の読み込みと表示) 課題 2a と 2b では,算術式を読み,表示し,値を求めるプログラムを作る. 限られた時間内で数式処理の本質部分を効率よく学ぶために, 入力できる算術式の構文を,次の BNF で定めるものに限る. 定数 ::= 0 | 1 | … | 9 変数 ::= a | b | … | z | A | B | … | Z 算術式 ::= 定数 | 変数 | (算術式+算術式) | (算術式*算術式) つまり,1桁の定数や1文字の変数の和や積からなる式が入力である. 入力された算術式を読み込み,2分木データとして格納し,そのデータをもとに 入力と同じ算術式を表示する,という処理 (下図) を繰り返すプログラムを作れ. (a+1) →→ (a+1) 次の要求を満たすものを作り,仕様の不明確な部分は各自で詳細化すること. ・算術式は標準入力から読む ・二つの算術式の区切りは一つの空白 (間隔文字や改行文字など) とする ・結果は標準出力に書く ・構文に合わない算術式を読んだらプログラムを終了する ・入力の終わりに達したらプログラムを終了する 算術式操作用のプログラムファイル exp.c の次の二つの関数を完成させること. ・算術式を読み込んで2分木に変換する関数 read_exp() ・算術式の2分木の表示関数 show_exp() 補足 ●基本課題 2b (数式の値の計算) 課題 2a のプログラムに,算術式の値を計算して表示する機能を追加せよ. ただし,変数の値は全て1であるとして,式の値を求めること. 例えば,入力が (2*3) と (x+(y+z)) の場合,次のような出力をすればよい. show: に続いて入力された算術式を,eval: に続いて算術式の値を表示している. (2*3) ←入力 show: (2*3) eval: 6 (x+(y+z)) ←入力 show: (x+(y+z)) eval: 3 まず,算術式の値を求める関数 eval_exp() (exp.c にある) を完成させる. 十分な数の検査データを使って動作の正しさを確かめよ. 補足 ●基本課題 2c (数式データの基本操作) 算術式の値を変えずに括弧をくくり直す簡単な式変形を考える.例えば,式 (1+(2+3)) (a*(b*c)) ((i*x)+((j*y)+(k*z))) の括弧を (結合法則を使って) 左にくくり直すと,それぞれ,次の式になる. ((1+2)+3) ((a*b)*c) (((i*x)+(j*y))+(k*z)) 括弧を左にくくり直せる (A+(B+C)) や (A*(B*C)) の形の算術式を ((A+B)+C) や ((A*B)*C) の形にする式変形は,式に対応する木を 「左に回転」する変形と捉えられる (下図).
→
この操作を実行する関数 rotate_left_exp() (exp.c にある) を完成させよ. この関数を使い,可能な場合にだけ算術式の括弧を左にくくり直す関数 assoc_left() を ex2.c に作って動作確認せよ. 補足 ────────────────────────────────────── ●発展課題 2d (進んだ数式操作) 入力の算術式に単項演算の - を使えるようにし,これまでに実現した 式変形に加えて,最外の - を消したり内側に移動したりする式変形を実現せよ. 関数 push_neg() を ex2.c に作ること. 例えば,式 --(a*-b) の - を消して (a*-b) にしたり, -((2*i)+(2*j)) の - を内側に分配して (-(2*i)+-(2*j)) にしたり, -((w+x)*(y+z)) の - を内側に移動して (-(w+x)*(y+z)) にする. 課題 2c と同様,外側に - があるときに1回だけ変形できればよい. 補足 ●発展課題 2e (値の割り当ての変更) プログラム実行開始時の各変数の値を1とし, (x=0) や (x=(x+(i*y))) といった式を入力することで 変数の値の割り当てが変えられるようにせよ. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 演習のホームページ 山田 俊行 https://www.cs.info.mie-u.ac.jp/~toshi/