Python(後置記法処理 改)
追加内容と作成コードは以下になります。
1.使用できる演算子の追加(**、//、%、sin、cos、tan、log、r)
2.符号混じり、浮動小数点数の入力に対応
3.後置記法で入力された数式を中置記法の式にして表示
4.エラー再実行時にスタックリストが初期化されるように修正
import math
_operators=["+", "-", "*","/","//","%","**"]
_tri_func = {"sin":math.sin,"cos":math.cos,"tan":math.tan}
_math_func = {"log":math.log,"r":math.sqrt}
_stack=[]
_infix=[]
_necessary_num = 2
_floating_point = 5
def push(x):
_stack.append(x)
def pop():
return _stack.pop()
def infix_push(x):
_infix.append(x)
def infix_pop():
return _infix.pop()
def err():
raise ValueError
def input_num_expression():
"""入力を求める関数"""
return input("後置記法で数式を入力してください:")
def get_num_expression(function):
"""<高階関数>入力を求める関数の値を判断する関数
@param function 入力処理を行う関数
@return tmp_list 認識可能なlist"""
while True:
s = function()
if s.lower() == "exit":
break
try:
if s == "":
err()
tmp_list = s.split(" ")
tmp_list = list(filter(lambda a:a is not "", tmp_list))
if len(tmp_list) < _necessary_num:
err()
validate_element(tmp_list)
return tmp_list
except:
print(f"「{s}」は無効な入力値です\n正しい入力値をお願いします")
return "exit"
def validate_element(xs):
"""<検査関数>有効な要素かどうか検査する関数
@param xs 入力値の各要素リスト
@return 各要素リスト"""
if xs == []:
return xs
elif _operators.count(xs[0]) == 1:
validate_element(xs[1:])
elif xs[0] in _tri_func:
validate_element(xs[1:])
elif xs[0] in _math_func:
validate_element(xs[1:])
elif validate_num(xs[0]):
validate_element(xs[1:])
else:
print("無効な入力エラー")
err()
def validate_num(s):
"""<検査関数>有効な数値かどうか検査する関数
@param s 文字列
@return 有効な数値の文字列"""
if s[0] == "-":
validate_num(s[1:])
elif s[0] == "+":
validate_num(s[1:])
elif s.count(".") == 1:
[a,b] = s.split(".")
if not a.isdigit() or not b.isdigit():
err()
return True
elif not s.isdigit():
err()
return True
def calculation(num_expression_list):
"""後置記法(逆ポーランド記法)の計算関数
@param num_expression_list 計算式のリスト
@retval True 計算終了結果表示
ErrorValue 計算不可能な式"""
print("Debug:",num_expression_list,_stack)
#print("infix:",_infix)
if num_expression_list == []:
if len(_stack) == 1:
print("\n実行結果",pop())
print("中置記法での式:",infix_pop(),"\n")
return True
else:
err()
elif _operators.count(num_expression_list[0]) == 1:
tmp_num1 = pop()
tmp_num2 = pop()
infix_num1 = infix_pop()
infix_num2 = infix_pop()
infix_push("("+infix_num2+num_expression_list[0]+infix_num1+")")
result = eval(tmp_num2+num_expression_list[0]+tmp_num1)
push(str(round(result,_floating_point)))
calculation(num_expression_list[1:])
elif num_expression_list[0] in _tri_func:
tmp_num = eval(pop())
infix_num = infix_pop()
infix_push(num_expression_list[0]+"("+infix_num+")")
result = _tri_func[num_expression_list[0]](math.radians(tmp_num))
push(str(round(result,_floating_point)))
calculation(num_expression_list[1:])
elif num_expression_list[0] in _math_func:
tmp_num = eval(pop())
infix_num = infix_pop()
infix_push(num_expression_list[0]+"("+infix_num+")")
result = _math_func[num_expression_list[0]](tmp_num)
push(str(round(result,_floating_point)))
calculation(num_expression_list[1:])
elif validate_num(num_expression_list[0]):
push(num_expression_list[0])
infix_push(num_expression_list[0])
calculation(num_expression_list[1:])
def test_exec():
while True:
num_expression = get_num_expression(input_num_expression)
if num_expression == "exit":
return False
try:
calculation(num_expression)
except:
print(f"「{num_expression}」は計算不可能な式です")
print("正しい入力値をお願いします")
_stack.clear()
_infix.clear()
def test():
while test_exec():
pass
print("end")
# 実行部分
test()
標準ライブラリのmathモジュールを利用する場合、sinやlogをキーとして辞書を作成し、inputで入力された要素が登録されているかどうかをinで評価することでチェックできるようにするとともに、辞書の値に数式の関数を登録して利用しやすい ようにしました。
中置表記への変換はスタックと同様のpop、pushが使えるリストを作成し、計算実行部分で計算結果を積むスタックとは別に処理する数式を積むようにして表示させました。
しかし、この方法では前置表記への変換は、また別の処理リストが必要となり、汎用性にかけているので、可能であれば木構造を作成し、どのようなパターンへの表記にも変化させることができるようにチャレンジしたいと思います。