🍩
入力した数式をPythonで逆ポーランド記法にして解く
サマリ
数式を入力して逆ポーランド記法にする過程をpythonで実装して、見える様にします。
マシンスペック
MacBook Air M2 arm64
逆ポーランド記法
プログラムで演算する際の記法の一種です。演算子を数字(非演算子)の列の後に記載する方法です。
今回はこれをpythonで可視化して実際に演算まで行います。
例えば1+1
という演算は11+
という記法に変わります。
これは人間にとってものすごく解りづらいのですが、コンピュータにとっては都合の良い記法になります。
コンピュータは、スタックを利用したアルゴリズムを採用しており、前から順番にスタックに積んでいき、数字の場合は連続して積み上げ、演算子の場合は必要な数字をスタックから取り出し演算、その後スタックに積み演算子がなくなるまで行います。
1*2+3*4
の逆ポーランド記法は1 2 * 3 4 * +
となります。
これをスタック[]に積んで擬似的に人間である私が演算をすると
手順 | スタックの中身 | 未処理トークン列 | 操作・説明 |
---|---|---|---|
1 | [] |
1 2 * 3 4 * + |
初期状態 |
2 | [1] |
2 * 3 4 * + |
1 を push |
3 | [1 2] |
* 3 4 * + |
2 を push |
4 | [2] |
3 4 * + |
* の演算子 → 1 × 2 を計算し 2 を push |
5 | [2 3] |
4 * + |
3 を push |
6 | [2 3 4] |
* + |
4 を push |
7 | [2 12] |
+ |
* の演算子 → 3 × 4 = 12 を push |
8 | [14] |
(空) |
+ の演算子 → 2 + 12 = 14 を push |
9 | 14 |
(終了) | 最後に残った値の 14 を pop |
結果は14となります。
このアルゴリズムをPythonで動かしてみます。
サンプルコード
import operator, re
OPS = {
'+': (1, operator.add),
'-': (1, operator.sub),
'*': (2, operator.mul),
'/': (2, operator.truediv),
}
token_pat = re.compile(r'\d+|[+\-*/()]')
def infix_to_rpn(expr: str) -> str:
out, op_stack = [], []
for tok in token_pat.findall(expr.replace(' ', '')):
if tok.isdigit():
out.append(tok)
elif tok in OPS:
prec, _ = OPS[tok]
while op_stack and op_stack[-1] in OPS and OPS[op_stack[-1]][0] >= prec:
out.append(op_stack.pop())
op_stack.append(tok)
elif tok == '(':
op_stack.append(tok)
elif tok == ')':
while op_stack and op_stack[-1] != '(':
out.append(op_stack.pop())
if not op_stack or op_stack.pop() != '(':
raise ValueError("mismatched parentheses")
out.extend(reversed(op_stack))
return ' '.join(out)
def eval_rpn(rpn: str) -> float:
stack = []
for tok in rpn.split():
if tok in OPS:
if len(stack) < 2:
raise ValueError("underflow")
b, a = stack.pop(), stack.pop()
stack.append(OPS[tok][1](a, b))
else:
stack.append(float(tok))
if len(stack) != 1:
raise ValueError("ill-formed RPN")
return stack[0]
if __name__ == "__main__":
print("数式を入力してください:")
expr = input()
rpn = infix_to_rpn(expr)
val = eval_rpn(rpn)
print(f"数式: {expr}\n 逆ポーランド記法: {rpn}\n = {val}")
こちらを実行すると、下記のように任意の数式を入力してEnterを押して逆ポーランド記法、演算を行います。
数式を入力してください:
1*2+3*4
数式: 1*2+3*4
逆ポーランド記法: 1 2 * 3 4 * +
= 14.0
期待した結果が出せました。
まとめ
逆ポーランド記法を紐解いて、コンピュータ側の計算アルゴリズムに寄り添いました。
Discussion