用Python最原始的函数模拟eval函数的浮点数运算功能

前几天看一个网友提问,如何计算'1+1'这种字符串的值,不能用eval函数.

我仿佛记得以前新手时,对这个问题完全不知道如何下手.

我觉得处理括号实在是太复杂了,多层嵌套括号怎么解析呢?一些多余的括号呢?

而在没有括号的情况下,处理不同运算符之间的优先级又很头疼.

而'**'这种占2个字符的运算符,还有着奇特的优先级规则,x**y**z应先计算y**z,并且如果z后面还跟着**的话,则不能先计算y**z..这是最难的地方!

 

但是,今天我竟然把它实现了!而且是用最原始的函数实现的,它们是:

str,s.isdigit,s.find,s.count,
dict,float
if,in,and,not
==,>=,+(字符串连接)

 

float对象的6个方法不计算在内,这是无法避免的基础函数.

而str,float,dict只是起到类型转换或初始化数据的作用.所以真正用到的函数更是少之又少.

可能正是因为如此,我在程序设计上花了很多的心思.我用了非常多的if判断.显得有些难以看懂.而实际上每个分支都对应一种简单的情况.

唯一难的地方在于幂的递归运算(get_rig_rest函数).

 

这令我非常激动.这让我意识到自己解决复杂问题的能力!

另外应该指出,我的思路来自于前段时间学习Scheme时重写一些基本函数所养成的递归思考的习惯.

同时,Python真的是太方便和强大了,快速的print让测试+修改十分简单,极大提高了大脑解决抽象问题的能力(没有print,也许你要浪费很多笔,纸和时间).

dic={'+':float.__add__,
     '-':float.__sub__,
     '*':float.__mul__,
     '/':float.__floordiv__,
     '%':float.__mod__,
     '**':float.__pow__,}

priority=dict((('**',3),('*',2),('/',2),('%',2),('+',1),('-',1)))

def prt(opf,p_opf):
    '幂运算符总是最优先,即使左边也是幂运算符'
    if p_opf=='**':
        return False
    return priority[opf]>=priority[p_opf]

def get_val(lef,opf,rig):
    return str(dic[opf](float(lef),float(rig)))

def isfloat(s):
    return s.isdigit() or s=='.'

def get_brace(s,start=0):
    pos=s.find(')',start)+1
    brace=s[:pos]
    if brace.count('(')==brace.count(')'):
        return brace,s[pos:]
    return get_brace(s,pos)

def get_rig_rest(s,num=''):
    '分裂为数字+剩余部分'
    '如果数字后是幂运算符,则需要计算该数字的幂,将结果作为数字部分返回'
    if s[:2] =='**':
        pow_num,rest=get_rig_rest(s[2:])
        return get_val(num,'**',pow_num),rest      
    if s=='' or s[0] in dic:
        return num,s
    if s[0]=='(':
        brace,rest=get_brace(s)
        return ieval(brace[1:-1]),rest
    if isfloat(s[0]):
        return get_rig_rest(s[1:],num+s[0])

def ieval(s,lef='',opf=None,rig=''):
    if not opf and not s:
        return lef
    if not opf and isfloat(s[0]):
        return ieval(s[1:],lef+s[0],opf,rig)
    if not opf and s[0] in dic:
        if s[1]=='*':
            return ieval(s[2:],lef,'**',rig)
        return ieval(s[1:],lef,s[0],rig)
    if not opf and s[0]=='(':
        brace,rest=get_brace(s)
        if not rest:
            return ieval(brace[1:-1])
        return ieval(rest,lef+ieval(brace[1:-1]),
                    opf,rig)
    if opf and not s:
        return get_val(lef,opf,rig)
    if opf and s[0]=='(':
        brace,rest=get_brace(s)
        if not rest:
            return get_val(lef,opf,ieval(brace[1:-1]))
        return ieval(lef+opf+ieval(brace[1:-1])+rest,
                    '',None,'')
    if opf and isfloat(s[0]):
        return ieval(s[1:],lef,opf,rig+s[0])
    if opf and s[0] in dic:
        number=2 if s[1]=='*' else 1
        p_opf=s[:number]
        if prt(opf,p_opf):
            return ieval(s,get_val(lef,opf,rig),None,'')
        p_rig,p_rest=get_rig_rest(s[number:])            
        return ieval(lef+opf+get_val(rig,p_opf,p_rig)+p_rest,
                    '',None,'')

test=['1+2+3', #常规
      '1+2*9', #简单运算符优先级
      '1+2*(3+4)', #简单运算符优先级+括号
      '4**3**2', #幂运算优先级
      '3+2*0.5**(2+2**2)',#多重优先级+括号
      '(1+2)*(2+3)', #并列括号
      '((9+3)/2)', #嵌套括号
      '((1234)-1)', #多余括号
      '((1+3**2)*((2%3+(3+3*2**3))*(4-1)*(1+2)+5*6)*4)'] #复杂情形

for x in test:
    print(ieval(x),'---',eval(x))
    

 

结果:

>>> 
6.0 --- 6
19.0 --- 19
15.0 --- 15
262144.0 --- 262144
3.03125 --- 3.03125
15.0 --- 15
6.0 --- 6.0
1233.0 --- 1233
11640.0 --- 11640

 

 

posted @ 2013-11-08 00:40  LisPythoniC  阅读(992)  评论(0编辑  收藏  举报