小说网 找小说 无限小说 烟雨红尘 幻想小说 酷文学 深夜书屋

使用Python运算一个字符串表达式

概述:

    如何运行一个表达式,例如:12+23*4/2这个我想大家都很了解。不过,如果这个表达式是一个字符串呢?或是这样来描述,一个表达式被写成了一个字符串,我们又应该如何去运行并求得值呢?你是否会想,如果我们能够拿到12, 23, 4, 2以及中间的运算符,那就是真的太好了。而事实上,我们也正是在朝着这个方向去努力。如果你学过算法或是数据结构,那我想这个小问题便不会阻止你前进了。

思路分析:

    如概述所说,如果我们能拿到字符串表达式中中各个我们肉眼能够识别的“元素”,就算我们迈过了第一步。什么!才第一步?是的,这才是第一步。那第二步是什么?
    没错,第二步就是运算。运算的过程会有一个难题,这个难题就是操作符的优先级问题,就拿上面的表达式来说,我们需要先进行23*4的运算,而不是12+23的运算。我们要做的就是让计算机按照我们认知中的优先来做运算。比较笨的方法就是依次遍历两遍表达式(事实上在后期的运算中,我们遍历的是一个列表)。第一遍先做乘除、第二遍再做加减。

元素分离:

    对于这一步操作在Python中进行的确很省事,因为我们可爱的Python可以在列表中存储不同类型的对象。这就有点像C中的结构体和Java中的类。我们首先得到的是一个个的字符,再对这些字符进行遍历和分类(是数字还是操作符)。

# split expression
def mixed_operation (exp):
    exp_list = list(exp)
    temp = ''
    behavor_list = []
    i = 0
    length = len(exp_list)
    for item in exp_list:
        if is_operation(item):
            behavor_list.append(int(temp))
            behavor_list.append(item)
            temp = ''
        else:
            temp += item

        if i == length - 1:
            behavor_list.append(int(temp))
            break;

        i += 1

    return behavor_list

逻辑运算:

    这个函数有一些特别,对于有编程经验的朋友可能已经不陌生。对,就是递归!对于一个coder,递归是必须掌握的,就让我们一起在合适的时候把递归搞起吧。这里写递归的原因就是我们的表达式中每一种运算符不可能只有一种。我们要一直这样遍历下去。这时可能你已经感觉到了,遍历的话,为什么不用for?是的,在我的代码里也有for的循环代码,不过这里的for可没有想像中那么好用,不信你可以试试看。

# Calculation op1 and op2('*' and '/' or '+' and '-')
def cal_op1_op2(exp_list, op1, op2):
    if len(exp_list) == 1:
        return exp_list
        
    i = 0
    has_op = False
    for i in range(2, len(exp_list), 2):
        a = exp_list[i - 2]
        o = exp_list[i - 1]
        b = exp_list[i]
        if o == op1 or o == op2:
            has_op = True
            exp_list[i - 2] = get_aob(a, o, b)
            del exp_list[i]
            del exp_list[i - 1]
            break

    if has_op == False:
        return exp_list

    return cal_op1_op2(exp_list, op1, op2)

特别说明:

    当然对于这个程序来说,并不是那么健壮。因为我们这些逻辑的前提是我们得到了一个正常的表达式,且不包含括号。所谓正常表达式,就是不会出现少数字或是少操作符或是输入了非'+'、'-'、'*'、'/'、[0-9]的字符或是字符集。我们的程序默认了一个理想的运行环境,因为只是说明代码的思路。

附完整代码:

#!/usr/bin/env python

'expression_cal.py -- cal the expression that you give to me'

# judgment a char is a operation or not
def is_operation(oper):
    if oper == '+' or oper == '-' or oper == '*' or oper == '/':
        return True
    else:
        return False

# split expression
def mixed_operation (exp):
    exp_list = list(exp)
    temp = ''
    behavor_list = []
    i = 0
    length = len(exp_list)
    for item in exp_list:
        if is_operation(item):
            behavor_list.append(int(temp))
            behavor_list.append(item)
            temp = ''
        else:
            temp += item

        if i == length - 1:
            behavor_list.append(int(temp))
            break;

        i += 1

    return behavor_list

# cal a o b
def get_aob(a, o, b):
    if o == '+':
        return a + b
    elif o == '-':
        return a - b
    elif o == '*':
        return a * b
    elif o == '/':
        return a / b

# Calculation op1 and op2('*' and '/' or '+' and '-')
def cal_op1_op2(exp_list, op1, op2):
    if len(exp_list) == 1:
        return exp_list
        
    i = 0
    has_op = False
    for i in range(2, len(exp_list), 2):
        a = exp_list[i - 2]
        o = exp_list[i - 1]
        b = exp_list[i]
        if o == op1 or o == op2:
            has_op = True
            exp_list[i - 2] = get_aob(a, o, b)
            del exp_list[i]
            del exp_list[i - 1]
            break

    if has_op == False:
        return exp_list

    return cal_op1_op2(exp_list, op1, op2)

# cal exp
def cal_exp(exp_list):
    exp_list = cal_op1_op2(exp_list, '*', '/')
    exp_list = cal_op1_op2(exp_list, '+', '-')
    
    return exp_list[0]

while True:
    expre = raw_input('Enter your expression(0 to end):\n')
    if expre == '0':
        break

    result = mixed_operation(expre)
    print 'list result = ',
    print result
    print cal_exp(result)

print 'END'

这里符一段《Python核心编程》中第11章中一个示例:算术游戏(easyMath.py)

#!/usr/bin/env python

from operator import add, sub
from random import randint, choice

ops = {'+': add, '-': sub}

MAXTRIES = 2

def doprob():
    op = choice('+-')
    nums = [randint(1, 10) for i in range(2)]
    nums.sort(reverse = True) # reverse nums by sorted
    ans = ops[op](*nums) # calculate the right result
    
    pr = '%d %s %d = ' % (nums[0], op, nums[1])
    oops = 0 # record calculate times
    while True:
        try:
            if int(raw_input(pr)) == ans:
                print 'Correct!'
                break
            if oops == MAXTRIES:
                print 'answer\n%s%d' % (pr, ans)
            else:
                print 'incorrect... try again'
                oops += 1
        except (KeyboardInterrupt, EOFError, ValueError):
            print 'invalid input... try again'
                
def main():
    while True:
        doprob()
        try:
            opt = raw_input('Again? [y]').lower()
            if opt and opt[0] == 'n':
                break
        except (KeyboardInterrupt, EOFError):
            break
            
if __name__ == '__main__':
    main()


注:在原有程序的基础上,我添加了两行注释。的确是感觉这个小程序非常的好,所以在我看到第11章的时候,就忍不住要把这个小程序放到我的博客中来。整个代码的风格也是很值得学习和借鉴的。

posted on 2015-04-09 21:20  王峰炬  阅读(1583)  评论(0编辑  收藏  举报

导航