逆ポーランド記法プログラムを勝手に少し書き換え

人の書いたプログラムにCHA-CHAを入れることでおなじみ(?)のPython初心者bonlifeです。
id:necoffeeさんが書いてた「前回の逆ポーランド記法プログラムの改良版」の書き方がすごく独特だったので、自分が読みやすい形に少し書き直してみました。
個人的に「ここを書き直した方が読みやすいんじゃないかな」と思ったポイントを何点か挙げてみます。

  1. リストの内容を1つずつ取り出す処理
    • while 内部で変数のインクリメントを行っている
    • for で値を取り出すようにした方が楽
  2. リストの最後の値の取得方法
    • List[len(List)-1] のようにリストの長さを使っている
    • List[-1] でOK
  3. リストの最後の値の削除
    • del(List[len(List)-1]) としている
    • 値を使うのであれば、List.pop() すればOK
  4. 演算子や括弧を格納しておくリストの処理
    • '@' という値を先頭において、それを見て判断している
    • リストが空かどうかで判断すれば'@'は不要

そんなこんなで、以下のようになりましたとさ。

  • 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勉強しなきゃっ!」て思った雨の金曜日。