逆ポーランド記法を用いた電卓 #Python
- 10 / 19 追記 少し修正しました(ゼロ除算, 型ヒント)
- 中置記法の式を入力すると後置記法に変換して計算します。
- 前回拙いC++で書いた逆ポーランド記法の電卓をPythonで書き直したので残しておきます。
目次
| 1 | 2 |
|---|---|
| 1 | 前置き |
| 2 | コード |
| 3 | 使い方 |
| 4 | 編集後記 |
| 5 | 参考サイト |
前置き
- 前回の電卓よりチェック関数の機能を強化しました。
- 例
・演算子の重複のチェック
・括弧の対応
・ピリオドの対応 など - 前回も書きましたが私は完全な趣味でプログラムを書いているので温かい目で見守ってください。
前回の記事を更新することはないと思います。その代わり自分が気になって修正するとしたらこっちの記事のプログラムだけです。- やる気が起きたらC++の方も直すかも
- Pythonのバージョン: uv 3.14.0(おそらく3.10以上は動く)
- 開発環境: Linux(Arch linux)
コード
- 以下が実際のコードです。
main.py
import sys
class PyCalculator:
def __init__(self) -> None:
# 入力された数式を格納する変数
self.formula: str = ""
# 完全な中置記法の式
self.infix_notation: list[str] = []
# 完全な後置記法の式
self.reverse_notation: list[str] = []
# 計算結果
self.result: str = ""
def run(self) -> None:
# 適切な数式が入力されているかの確認
self._check()
# 中置記法から後置記法に変換
self._parser()
# 後置記法の式を計算
self.result = self._calculate()
def input(self) -> None:
# 入力された数式の空白を削除
self.formula: str = input("式 > ").replace(" ", "")
def _check(self) -> None:
"""
計算できない文字または不適当な入力のチェック
完全な中置記法を担保する
"""
#数字のカウントする変数
num_count: int = 0
# 括弧の対をカウントする変数
brackets_count: int = 0
# 演算子をカウントする変数
operator_count: int = 0
# 負の数の判断
negative: bool = True
# 演算子の判断
operate: bool = False
# 数字部分の一時保存
unitnum: str = ""
# 適切な数式が入力されているかの確認
for i in self.formula:
# 数字または演算子または括弧以外を省く
if not i.isdigit() and i not in ".+-*/^()":
print("入力が正しくありません")
sys.exit()
# 数字またはピリオドのとき
if i.isdigit() or i == ".":
unitnum += i
continue
# 演算子または括弧のとき
if i in "+-*/^()":
# 数字を保存
if unitnum != "":
# 先頭または単体でピリオドの場合
if unitnum[0] == ".":
print("数字の先頭がピリオドで始まっています")
sys.exit()
# 末尾にピリオドの場合
elif unitnum[-1] == ".":
print("数字の最後がピリオドで終わっています")
sys.exit()
# チェックが終了して数字を保存
else:
self.infix_notation.append(unitnum)
unitnum = ""
negative = False
operate = False
num_count += 1
# 記号の重複の判別
if i in "+*/^" and operate:
print("記号が重複して入力されています")
sys.exit()
# 負の数を対応
elif i == "-" and negative:
unitnum += i
continue
# 括弧が正しく対をなしているか判別
elif i in "()":
# 左括弧のとき
if i == "(":
# 右括弧以外
if self.infix_notation and self.infix_notation[-1] not in "+-*/^(":
print("入力が正しくありません")
sys.exit()
brackets_count += 1
# 右括弧のとき
elif i == ")":
brackets_count -= 1
# 右括弧が先に出てきたとき
if brackets_count < 0:
print("括弧が正しく対になっていません")
sys.exit()
# 括弧のときの処理
self.infix_notation.append(i)
negative = True
continue
# 演算子(括弧以外)のときの処理
self.infix_notation.append(i)
operator_count += 1
negative = True
operate = True
# unitnumに残った数字を代入
if unitnum != "":
self.infix_notation.append(unitnum)
num_count += 1
# 括弧の数が正しく対になっていないとき
if brackets_count != 0:
print("括弧が正しく対になっていません")
sys.exit()
# 演算子と数字の数を比較して正しい数になっているか
if num_count != operator_count + 1:
print("正しい数字または演算子の数ではありません")
sys.exit()
def _parser(self) -> None:
"""
中置記法から後置記法に変換
"""
# 演算子の一時保存
stack: list[str] = []
# 演算子の優先順位
operatorlist: dict[str, int] ={ "+": 1, "-": 1, "*": 2, "/": 2, "^": 3 }
# 中置記法から後置記法に変換
for i in self.infix_notation:
# 数字のとき
if self._is_float(i):
self.reverse_notation.append(i)
continue
# 最初の演算子または括弧の中での初めての演算子のとき
elif not stack or stack[-1] == "(":
stack.append(i)
# 左括弧のとき
elif i == "(":
stack.append(i)
# 右括弧のとき
elif i == ")":
# 右括弧まで演算子を吐き出す
while stack and stack[-1] != "(":
self.reverse_notation.append(stack[-1])
del stack[-1]
# 左括弧の削除
del stack[-1]
continue
# 今の演算子の方が優先順位が高いとき(例: + < *)
elif operatorlist[stack[-1]] < operatorlist[i]:
stack.append(i)
# 今の演算子の優先順位が低い(または同等)とき(例: + == -)
else:
while stack and stack[-1] != "(" and operatorlist[stack[-1]] >= operatorlist[i]:
self.reverse_notation.append(stack[-1])
del stack[-1]
stack.append(i)
# スタックに残った演算子を追加
while stack:
self.reverse_notation.append(stack[-1])
del stack[-1]
def _calculate(self) -> str:
# 計算式の一時的保存
stack: list[str] = []
# 計算用変数
x: float = 0
y: float = 0
for i in self.reverse_notation:
# 演算子のとき
if i in "+-*/^":
# 数字に変換
y = float(stack[-1])
del stack[-1]
x = float(stack[-1])
del stack[-1]
# 計算
match i:
case "+":
stack.append(str(x + y))
case "-":
stack.append(str(x - y))
case "*":
stack.append(str(x * y))
case "/":
# ゼロ除算を省く
if y == 0:
print("ゼロ除算はできません")
sys.exit()
stack.append(str(x / y))
case "^":
stack.append(str(x ** y))
# 数字をスタックに代入
else:
stack.append(i)
# 計算結果を返す
return stack[-1]
# 結果の表示
def output_result(self) -> None:
# 中置記法の式を表示
print("中置記法の式 > ", end=" ")
for i in self.infix_notation:
print(i, end=" ")
print("")
# 後置記法の式を表示
print("後置記法の式 > ", end=" ")
for i in self.reverse_notation:
print(i, end=" ")
print("")
# 結果を表示
print("結果: ", self.result)
# ヘルパー関数
# 数字(浮動小数点型)に変換できるかを判別
def _is_float(self, s: str) -> bool:
try:
float(s)
except ValueError:
return False
else:
return True
if __name__ == "__main__":
# インスタンスの作成
instance = PyCalculator()
# 式の入力
instance.input()
# 変換と計算
instance.run()
# 結果の表示
instance.output_result()
- 手順は前のプログラムと変わりませんが、よりチェック機能を豊富にしたので少しはマシになっているのではないかと。
使い方
- これも前回と大きくは変わっていません。
bash
# Linuxでの実行
python main.py
式 > 238 + 3948.763 - ( 3847 * 2 / 3 ) <- ここに入力
中置記法の式 > 238 + 3948.763 - ( 3847 * 2 / 3 )
後置記法の式 > 238 3948.763 + 3847 2 * 3 / -
結果: 1622.0963333333334
編集後記
- やっぱりPythonから始めていれば2回も書くことなかったのに...
- おそらくまだ抜け穴的な式もあると思うので半信半疑ぐらいで参考にしてください
参考サイト
- 前回のプログラム(あんまり参考にならない)