逆ポーランド記法プログラムを勝手に少し書き換え
人の書いたプログラムにCHA-CHAを入れることでおなじみ(?)のPython初心者bonlifeです。
id:necoffeeさんが書いてた「前回の逆ポーランド記法プログラムの改良版」の書き方がすごく独特だったので、自分が読みやすい形に少し書き直してみました。
個人的に「ここを書き直した方が読みやすいんじゃないかな」と思ったポイントを何点か挙げてみます。
- リストの内容を1つずつ取り出す処理
- while 内部で変数のインクリメントを行っている
- for で値を取り出すようにした方が楽
- リストの最後の値の取得方法
- List[len(List)-1] のようにリストの長さを使っている
- List[-1] でOK
- リストの最後の値の削除
- del(List[len(List)-1]) としている
- 値を使うのであれば、List.pop() すればOK
- 演算子や括弧を格納しておくリストの処理
- '@' という値を先頭において、それを見て判断している
- リストが空かどうかで判断すれば'@'は不要
そんなこんなで、以下のようになりましたとさ。
- reverse_polish.py
#-*- coding: cp932 -*- from __future__ import division import re def check_words_for_calc(words): p = re.compile(r'(^[0-9]+([.][0-9]+)?)|([+*/()-])$') for i in words: if not p.match(i): return False return True def read_a_line(): p = re.compile(r'^([0-9a-z_().+*/-]|\s)+$',re.IGNORECASE) while True: inp = raw_input('Please input formula : ') if p.match(inp): break else: print "WAR : incorrect input" continue p = re.compile(r'([^0-9a-z_.])',re.IGNORECASE) words = [ x.strip() for x in p.split(inp) if x.strip() != '' ] return words def less_or_equal_prec(a,b): if a in '*/' and b in '*/': return True elif a in '+-' and b in '+-*/': return True else: return False def calc(out): if check_words_for_calc(out): stack = list() for i in range(len(out)): if out[i] not in '+-*/': stack.append(out[i]) else: try: temp = eval(stack.pop(-2) + out[i] + stack.pop()) stack.append(str(temp)) except ZeroDivisionError: return "Zero division error." except IndexError: return "Failed calculation. (input formula may be wrong)" except SyntaxError: return "Can't calculate." if len(stack) == 1: return stack[0] else: return "Failed calculation. (input formula may be wrong)" else: return "Can't calculate." def reverse_polish(): words = read_a_line() out = list() stack = list() for i in range(len(words)): if words[i] not in '+-*/()': out.append(words[i]) elif words[i] == '(': stack.append(words[i]) elif words[i] == ')': while stack[-1] != '(': out.append(stack.pop()) stack.pop() elif words[i] in '+-*/': while stack: if less_or_equal_prec(words[i],stack[-1]): out.append(stack.pop()) else: break stack.append(words[i]) while stack: out.append(stack.pop()) print "reverse polish notation : %s " % ' '.join(out) print "calculation result : %s " % calc(out)
自分で書いておきながらダメ出しするのもアレですが、エラー処理とかいつにも増して適当です。なんとも場当たり的。エラー処理を関数として括りだした方が読みやすい気がしますね。後は calc() が相当汚い…orz 使う必要がない eval() 使ったあたりが特にダメっぽい。
で、以下のように使います。
In [1]: from reverse_polish import reverse_polish In [2]: reverse_polish() Please input formula : 1 + 2 * 3 reverse polish notation : 1 2 3 * + calculation result : 7 In [2]: reverse_polish() Please input formula : ( 2.5 * 5 ) / 2 reverse polish notation : 2.5 5 * 2 / calculation result : 6.25 In [3]: reverse_polish() Please input formula : 1 / 0 reverse polish notation : 1 0 / calculation result : Zero division error. In [4]: reverse_polish() Please input formula : 1 / 0.00 reverse polish notation : 1 0.00 / calculation result : Zero division error. In [5]: reverse_polish() Please input formula : A * ( B - C / D ) reverse polish notation : A B C D / - * calculation result : Can't calculate. In [6]: reverse_polish() Please input formula : 1 2 3 4 5 reverse polish notation : 1 2 3 4 5 calculation result : Failed calculation. (input formula may be wrong) In [7]: reverse_polish() Please input formula : + + + + + reverse polish notation : + + + + + calculation result : Failed calculation. (input formula may be wrong) In [8]: reverse_polish() Please input formula : ( 5 * 2 reverse polish notation : 5 2 * ( calculation result : Failed calculation. (input formula may be wrong)
なんだか「ちゃんとPython勉強しなきゃっ!」て思った雨の金曜日。