式の表し方とスタックの応用
中置の式を逆ポーランド表記(後置表記)に直す問題と、後置表記の式をスタックで計算してスタックの深さを比べる問題を解けるようになる。あわせて、3アドレス命令・四つ組の列を元の式に戻す問題と、正規表現の + と * を読む問題も自力で解けるようにする。
コンピュータは「(A+B)×C」のような括弧つきの式をそのままでは計算しにくいので、コンパイラ(プログラムを機械語に翻訳するソフト)は式を後置表記や3アドレス命令に直してから、スタックで計算します。手順どおりに機械的に解ける問題なので、やり方を一度覚えれば毎回得点になる分野です。
このレッスンで稽古する(用語チェック・過去問)結論:このレッスンで覚える4つ
- 後置表記(逆ポーランド表記)は、演算子を2つの数の後ろに置く書き方です。括弧が要らなくなります。出題数がいちばん多い知識です。
- 後置表記はスタックで計算します。数が来たら積み、演算子が来たら上から2つ取り出して計算し、結果を積み直します。
- 3アドレス命令・四つ組は、計算を1ステップずつ並べた形です。上から順に代入していけば元の式に戻せます。
- 正規表現では、**+ は「1回以上」、* は「0回以上」**の繰返しです。
中置・後置・前置の違い
ふだん使う「a+b」は中置記法(演算子を数の間に置く書き方)です。試験でよく出るのは**後置記法(逆ポーランド記法)**で、同じ式を「ab+」と書きます。
| 記法 | 別名 | 演算子の位置 | a+b の書き方 | (a+b)*c の書き方 | 括弧 |
|---|---|---|---|---|---|
| 中置記法 | ― | 数の間 | a+b | (a+b)*c | 必要 |
| 後置記法 | 逆ポーランド記法 | 数の後ろ | ab+ | ab+c* | 不要 |
| 前置記法 | ポーランド記法 | 数の前 | +ab | *+abc | 不要 |
「逆」が付いているほうが後置です。名前を取り違えさせる選択肢に注意しましょう。
用語の確認です。演算子は +、−、×、÷ などの記号、**被演算子(オペランド)**は計算される数や変数(a、b など)のことです。
中置から後置への直し方(いちばん出る)
コツは「最後に計算する演算子を、いちばん後ろに置く」ことです。これを括弧の内側に向かって繰り返します。
手順
- 式全体で最後に計算される演算子を探す(括弧の外にあり、優先順位がいちばん低いもの。× ÷ は + − より先に計算される)
- 「[左側の後置] [右側の後置] [その演算子]」の形にする
- 左側と右側にも同じ手順を当てはめる。変数1文字になったら終わり
式を木の形にすると分かりやすくなります。下の木は (A+B)×(C−D÷E) です。上にあるほど後に計算する演算子です。
flowchart TD
M["×(最後に計算)"] --> P["+"]
M --> S["−"]
P --> A["A"]
P --> B["B"]
S --> C["C"]
S --> V["÷"]
V --> D["D"]
V --> E["E"]
この木を「左 → 右 → 自分」の順に読むと AB+CDE÷−× になり、これが後置表記です。
**被演算子(A、B、C…)の並び順は、元の式から変わりません。**選択肢で文字の順番が入れ替わっていたら(ED÷、BA+ など)、その時点で疑いましょう。
後置表記をスタックで計算する
後置表記は左から1文字ずつ読み、次のルールで処理します。
| 読んだもの | スタックへの操作 |
|---|---|
| 被演算子(a、b…) | プッシュ(上に積む) |
| 演算子(+、* など) | 上から2つをポップ(取り出す)して計算し、結果をプッシュする |
演算子を処理するときは「先に取り出したほうが右側」です。下にあった数(後から取り出す)が左側になります。引き算と割り算では順番を間違えないようにしましょう。
書類トレイに積んだ書類を思い浮かべてください。演算子が来るたびに上の2枚をまとめて1枚にするので、演算子が早く出てくる式ほど、トレイ(スタック)は高く積み上がりません。この性質が「スタックの深さ」の問題で問われます。
なぜ計算にスタックを使うのか
計算の途中結果をいったん積んでおき、別の計算を済ませてから取り出して合わせる、という処理はLIFO(後入れ先出し)の動きそのものです。そのためスタックが向いています。
| データ構造 | 取り出す順 | 向いている処理 |
|---|---|---|
| スタック | LIFO(最後に入れたものから) | 途中結果を退避し、別の計算の後で戻して計算する |
| キュー | FIFO(最初に入れたものから) | 入れた順番どおりに取り出す |
| 配列・リスト | 位置を指定する | 中ほどの要素を変更したり、挿入したりする |
3アドレス命令・四つ組から式に戻す
3アドレス命令は「c = a op b」のように、被演算子2つと結果1つの合計3つの場所(アドレス)を持つ命令です。四つ組は同じ内容を(演算子, 被演算子1, 被演算子2, 結果)の4つ組で書いたものです。どちらもコンパイラが構文解析(式の構造を読み取ること)をした結果として出てきます。
解き方は、上から順に一時変数を中身で置き換えていくだけです。最後に「括弧を付けないと優先順位が変わってしまう部分」に括弧を付けます。一時変数(T1、w1 など)が残っている選択肢は、式として未完成なので正解にはなりません。
正規表現の + と *(少なめ)
正規表現は、文字列のパターンを記号で表す書き方です。[A−Z] は大文字1文字、[0−9] は数字1文字を表します。
| 記号 | 意味 | [0−9]○ の例 |
|---|---|---|
| + | 直前のものを1回以上繰り返す | [0−9]+ は数字が最低1文字必要 |
| * | 直前のものを0回以上繰り返す | [0−9]* は数字が無くてもよい |
- と * は繰返しの指示です。文字そのものではありません。
試験での問われ方
- 後置への変換(最頻出):演算子の位置だけ、または被演算子の順番だけをずらした、見た目の似た選択肢が並びます。選択肢をスタックで計算して中置に戻し、問題の式と一致するかを確かめるのが確実です。
- スタックの深さ:「最大の深さが最も少ない式はどれか」と聞かれます。各選択肢でスタックに積まれる個数を数えます。
- 3アドレス命令・四つ組:一時変数が残った式や、括弧が抜けて優先順位が変わった式がひっかけです。
- スタックの用途:キュー(FIFO)の説明や、配列・リストの操作の説明が誤りの選択肢として混ぜられます。
- 正規表現:+ や * を文字として含む文字列や、必須の部分(+ が付いた部分)が欠けた文字列が、誤りの選択肢になります。
中置 → 後置:Y=(A+B)×(C−D÷E)
ア YAB+C−DE÷×=
イ YAB+CDE÷−×=
ウ YAB+EDC÷−×=
エ YBA+CD−E÷×=
解き方と答えを見る
- 代入の「=」はいちばん最後に行うので、例の YAB−C×= と同じく、Y を先頭に、= を末尾に置く。形は Y[右辺の後置]=。
- 右辺 (A+B)×(C−D÷E) で最後に計算するのは、括弧の外にある ×。形は[(A+B)の後置][(C−D÷E)の後置]×。
- 左側 A+B → AB+。
- 右側 C−D÷E では ÷ を先、− を後に計算する。最後に計算する − を末尾に置いて C[D÷E の後置]− とし、D÷E → DE÷ なので CDE÷− になる。
- つなげると AB+ CDE÷− × で、全体は YAB+CDE÷−×=。これはイ。
- 確認:アは ((A+B)−C)×(D÷E)、ウは (A+B)×(E−D÷C)、エは (B+A)×((C−D)÷E) を表しており、どれも問題の式と違う。
答え:イ(YAB+CDE÷−×=)
後置 → 中置に戻して照合する
| 計算式 | 逆ポーランド表記法 | |
|---|---|---|
| ア | ((a+b)*c)-d | abc*+d- |
| イ | (a+(b*c))-d | ab+c*d- |
| ウ | (a+b)*(c-d) | abc*d-+ |
| エ | a+(b*(c-d)) | abcd-*+ |
ア 計算式: ((a+b)*c)-d / 逆ポーランド表記法: abc*+d-
イ 計算式: (a+(b*c))-d / 逆ポーランド表記法: ab+c*d-
ウ 計算式: (a+b)*(c-d) / 逆ポーランド表記法: abc*d-+
エ 計算式: a+(b*(c-d)) / 逆ポーランド表記法: abcd-*+
解き方と答えを見る
- 各選択肢の後置表記をスタックで計算して中置に戻し、左の計算式と比べる。
- ア abc*+d−:a、b、c を積む → * で b*c → + で a+b*c → d を積む → − で (a+(b*c))−d。これはイの計算式なので、アの組合せは不一致。
- イ ab+c*d−:a+b → *c で (a+b)*c → −d で ((a+b)*c)−d。これはアの計算式なので不一致(アとイの後置表記が入れ替わっている)。
- ウ abc*d−+:b*c → −d で (b*c)−d → + で a+((b*c)−d)。(a+b)*(c−d) ではないので不一致。
- エ abcd−*+:a、b、c、d を積む → − で c−d → * で b*(c−d) → + で a+(b*(c−d))。計算式と一致する。
答え:エ
スタックの最大の深さを比べる
ア ab+c+d+
イ ab+cd++
ウ abc++d+
エ abc+d++
解き方と答えを見る
- ルール:被演算子はプッシュ(深さ+1)、演算子は2つポップして1つプッシュ(深さ−1)。
- ア ab+c+d+:a(1) b(2) +(1) c(2) +(1) d(2) +(1)。最大の深さは 2。
- イ ab+cd++:a(1) b(2) +(1) c(2) d(3) +(2) +(1)。最大の深さは 3。
- ウ abc++d+:a(1) b(2) c(3) +(2) +(1) d(2) +(1)。最大の深さは 3。
- エ abc+d++:a(1) b(2) c(3) +(2) d(3) +(2) +(1)。最大の深さは 3。
- 最大の深さがいちばん少ないのは 2 のア。被演算子を2つ積むたびにすぐ演算子で1つにまとめているため。
答え:ア
四つ組を式に戻す
ア A+B*C/D
イ A+B*C/T2
ウ B*C+A/D
エ B*C+T1/D
解き方と答えを見る
- (*, B, C, T1) → T1 = B*C
- (/, T1, D, T2) → T2 = T1/D = B*C/D
- (+, A, T2, T3) → T3 = A + T2 = A + B*C/D
- ア A+B*C/D:優先順位どおりに B*C/D を先に計算し、最後に A を足す式なので、T3 と一致する。
- イ・エは一時変数 T2・T1 が残っていて、式として未完成。
- ウは B*C+A/D で、B*C + (A/D) と読める。問題の四つ組が表す A + (B*C)/D とは別の式なので、一致しない。
答え:ア
3アドレス命令を式に戻す
ア b+c/d*e/f-g
イ b+c/d*(e/f-g)
ウ (b+c/d)*e/f-g
エ (b+c/d)*(e/f-g)
解き方と答えを見る
- /(c, d, w1) → w1 = c/d
- +(b, w1, w2) → w2 = b+c/d
- /(e, f, w3) → w3 = e/f
- -(w3, g, w4) → w4 = e/f-g
- *(w2, w4, x) → x = w2*w4。* は + や - より先に計算されるので、両側を括弧で囲む必要があり、x = (b+c/d)*(e/f-g) になる。
- イは b に掛けていない。ウは * が e にしか掛からず、-g が掛け算の外に出ている。アは括弧が無い。いずれも別の式。
答え:エ
正規表現 [A−Z]+[0−9]* に当てはまる文字列
ア 456789
イ ABC+99
ウ ABC99*
エ ABCDEF
解き方と答えを見る
- パターンを分ける:前半は大文字が1文字以上(必須)、後半は数字が0文字以上(無くてもよい)。
- ア 456789:先頭が数字で、必須の大文字が1文字も無いので不適。
- イ ABC+99:+ は繰返しの記号であって、文字としては許されていない。不適。
- ウ ABC99*:同じく * が文字として入っているので不適。
- エ ABCDEF:大文字6文字+数字0文字で、パターンに当てはまる。
答え:エ
要点(試験直前に読む)
- 後置記法(逆ポーランド記法)=演算子を後ろに置く。「最後に計算する演算子を末尾へ」を括弧の内側に向かって繰り返す。
- 被演算子の並び順は中置でも後置でも変わらない。変わるのは演算子の位置だけ。
- 後置記法の計算:被演算子はプッシュ、演算子は2つポップして計算し、結果をプッシュ。演算子が早く出てくる式ほどスタックは浅い。
- 途中結果を退避して後で戻して計算する処理には、LIFO のスタックが向いている(入れた順に出すならキュー)。
- 3アドレス命令・四つ組は上から順に一時変数を置き換え、優先順位が変わる部分には括弧を付ける。
- 正規表現:+は1回以上(必須)、*は0回以上(無くてもよい)。
ひっかけ
- 後置表記の選択肢どうしで式が入れ替わっていることがある。選択肢をスタックで中置に戻して照合すれば確実に見破れる。
- 被演算子の順番が入れ替わった選択肢(ED÷、BA+ など)に注意。引き算・割り算では結果が変わる。
- 「格納された順番に取り出す」はキュー(FIFO)の説明で、スタックの用途ではない。
- 四つ組・3アドレス命令の問題では、一時変数が残った式と、括弧が抜けて優先順位が変わった式がひっかけ。B*C+A/D は B*C + (A/D) と読む。
- 正規表現で + や * を文字として含む文字列は不適。+が付いた必須の部分が欠けている文字列(数字だけなど)も不適。
用語(18)
最後に入れたデータから順に取り出す(後入れ先出し)データ構造。
⚠ キューは先に入れたものから取り出す。「最後に入れたものが先に出る」ならスタック。
最後に入れたものを最初に取り出す方式で、スタックの性質。
⚠ FIFO(先入れ先出し)はキューの性質。L=Last が先に出るほうがスタック。
スタックの一番上にデータを積む操作がプッシュ、一番上から取り出す操作がポップ。
⚠ キューでは同じ働きの操作をエンキュー/デキューと呼ぶ。
先に入れたデータから順に取り出す(先入れ先出し)データ構造。
⚠ 「格納された順番に取り出す」はキュー。スタックと取り違えやすい。
a+b のように演算子を被演算子の間に置く、ふだんの書き方。括弧が必要になる。
⚠ 後置・前置は括弧が要らない。
ab+ のように演算子を被演算子の後ろに置く書き方で、スタックで左から順に計算できる。
⚠ 前置記法(ポーランド記法)は演算子が前に来る。「逆」が付いているほうが後置。
+ab のように演算子を被演算子の前に置く書き方。
⚠ 逆ポーランド記法(後置)と名前が似ているので、「逆」の有無で区別する。
+、−、×、÷ など、計算の種類を表す記号。
⚠ 被演算子は計算される側の数や変数。
演算子によって計算される数や変数のこと。
⚠ 後置記法に直しても被演算子の並び順は変わらない。変わるのは演算子の位置だけ。
括弧が無いときにどの演算子から先に計算するかの決まりで、通常は × ÷ が + − より先。
⚠ 括弧があれば括弧の中が優先される。式を復元するとき、括弧を付け忘れると別の式になる。
プログラム全体をまとめて機械語などに翻訳するソフトウェア。
⚠ インタプリタは1行ずつ解釈しながら実行する。
コンパイラが、プログラムや式の文法上の構造(どの演算をどの順で行うか)を読み取る処理。
⚠ 字句解析は、文字の並びを単語(トークン)に区切る前段階の処理。
c = a op b のように、被演算子2つと結果1つの計3つの場所を指定して演算を1つだけ行う命令。
⚠ 四つ組は同じ内容を(演算子, 被演算子1, 被演算子2, 結果)の順で書いたもの。書く順番が違うだけ。
(演算子, 被演算子1, 被演算子2, 結果)の4項目で1つの演算を表す、構文解析結果の表現形式。
⚠ 3アドレス命令と中身は同じ。項目の順番を問題文で確認する。
計算の途中結果を一時的に入れておく変数(T1、w1 など)。
⚠ 最終的な式には残らない。残っている選択肢は誤り。
文字列のパターンを、記号を使って表す書き方。
直前のパターンを1回以上繰り返すことを表す記号。
⚠ *は0回以上。+が付いた部分は最低1回必要で、*が付いた部分は無くてもよい。
直前のパターンを0回以上繰り返すことを表す記号。
⚠ +(1回以上)と取り違えやすい。どちらも文字そのものではなく繰返しの指示。
このレッスンの過去問
- スタックの適用処理(計算途中結果の退避)平成26年度 秋期 基本情報技術者試験 午前 問5
- 逆ポーランド表記(後置表記)の式変換平成26年度 秋期 基本情報技術者試験 午前 問4
- 3アドレス命令列から式への復元平成27年度 秋期 基本情報技術者試験 午前 問19
- 正規表現の記法と文字列集合の判定平成28年度 春期 基本情報技術者試験 午前 問3