用Python3实现表达式求值
一、题目描述
请用 python3 编写一个计算器的控制台程序,支持加减乘除、乘方、括号、小数点,运算符优先级为括号>乘方>乘除>加减,同级别运算按照从左向右的顺序计算。
二、输入描述
- 数字包括"0123456789",小数点为".",运算符包括:加("+")、减("-")、乘("*")、除("/")、乘方("^",注:不是**!)、括号("()")
- 需要从命令行参数读入输入,例如提交文件为 main.py,可以用 python3 main.py "1+2-3+4" 的方式进行调用
- 输入需要支持空格,即 python3 main.py "1 + 2 - 3 + 4" 也需要程序能够正确给出结果
- 所有测试用例中参与运算的非零运算数的绝对值范围保证在 10^9-10^(-10) 之内, 应该输出运算结果时非零运算结果绝对值也保证在该范围内
三、输出描述
- 数字需要支持小数点,输出结果取10位有效数字,有效数字位数不足时不能补0
- 对于不在输入描述内的输入,输出INPUT ERROR
- 对于格式不合法(例如括号不匹配等)的输入,输出 FORMAT ERROR
- 对于不符合运算符接收的参数范围(例如除0等)的输入,输出VALUE ERROR
- 对于2、3、4的情况,输出即可,不能抛出异常
- 同时满足2、3、4中多个条件时,以序号小的为准
四、样例
输入: 1 + 2 - 3 + 4
输出: 4
输入: 1 + 2 - 3 + 1 / 3
输出: 0.3333333333
输入: 1 + + 2
输出: FORMAT ERROR
输入: 1 / 0
输出: VALUE ERROR
输入: a + 1
输出: INPUT ERROR
【注:此题为TsinghuaX:34100325X 《软件工程》 MOOC 课程 Spring, 2016 Chapter 1 Problem,此文发布时,这门课刚刚在 “学堂在线” 上开课两天】
用 Python3 实现,初看一下,首先想到的其实是一种“讨巧”(作弊 >_<)的方法(由于曾经网站被挂码的悲壮历史……),即通过 eval() 函数(这应该是黑客用得最多的一个函数了吧+_+)直接求解表达式,谁叫题目指定用 Python 这种方便的脚本语言呢~
大致代码区区几行:
from sys import argv if __name__ == "__main__":
exp = argv[1]
print(eval(exp))
即便是考虑到题干中的输出要求做异常处理(try...except)输出相应的错误信息,也就十行左右。
但稍深入就会发现,有些情形还是无法按要求处理的,比如样例 “1 + + 2”,用 eval() 会输出结果 “3” (+2 前的 “+” 被当作正号),而题目要求输出 “FORMAT ERROR”。
没办法,只能老老实实做苦力活儿了。
表达式求值其实是《数据结构》课程里一个基本且重要的问题之一,一般作为 “栈” 的应用来提出。
问题的关键就是需要按照人们通常理解的运算符的优先级来进行计算,而在计算过程中的临时结果则用 栈 来存储。
为此,我们可以首先构造一个 “表” 来存储当不同的运算符 “相遇” 时,它们谁更 “屌” 一些(优先级更高一些)。这样就可以告诉计算机,面对不同的情形,它接下来应该如何来处理。
其次,我们需要构造两个栈,一个运算符栈,一个运算数栈。
运算符栈是为了搞定当某个运算符优先级较低时,暂时先让它呆在栈的底部位置,待它可以 “重见天日” 的那一天(优先级相对较高时),再把它拿出来使用。正确计算完成后,此栈应为空。
运算数栈则是为了按合理的计算顺序存储运算中间结果。正确计算完成后,此栈应只剩下一个数,即为最后的结果。
完整的代码如下:
# -*- coding: utf-8 -*- #################################
# @Author: Maples7
# @LaunchTime: 2016/2/24 12:32:38
# @FileName: main
# @Email: maples7@163.com
# @Function:
#
# A Python Calculator for Operator +-*/()^
#
################################# from sys import argv
from decimal import * def delBlank(str):
"""
Delete all blanks in the str
"""
ans = ""
for e in str:
if e != " ":
ans += e
return ans def precede(a, b):
"""
Compare the prior of operator a and b
"""
# the prior of operator
prior = (
# '+' '-' '*' '/' '(' ')' '^' '#'
('>', '>', '<', '<', '<', '>', '<', '>'), # '+'
('>', '>', '<', '<', '<', '>', '<', '>'), # '-'
('>', '>', '>', '>', '<', '>', '<', '>'), # '*'
('>', '>', '>', '>', '<', '>', '<', '>'), # '/'
('<', '<', '<', '<', '<', '=', '<', ' '), # '('
('>', '>', '>', '>', ' ', '>', '>', '>'), # ')'
('>', '>', '>', '>', '<', '>', '>', '>'), # '^'
('<', '<', '<', '<', '<', ' ', '<', '=') # '#'
) # operator to index of prior[8][8]
char2num = {
'+': 0,
'-': 1,
'*': 2,
'/': 3,
'(': 4,
')': 5,
'^': 6,
'#': 7
} return prior[char2num[a]][char2num[b]] def operate(a, b, operator):
"""
Operate [a operator b]
"""
if operator == '+':
ans = a + b
elif operator == '-':
ans = a - b
elif operator == '*':
ans = a * b
elif operator == '/':
if b == 0:
ans = "VALUE ERROR"
else:
ans = a / b
elif operator == '^':
if a == 0 and b == 0:
ans = "VALUE ERROR"
else:
ans = a ** b return ans def calc(exp):
"""
Calculate the ans of exp
"""
exp += '#'
operSet = "+-*/^()#"
stackOfOperator, stackOfNum = ['#'], []
pos, ans, index, length = 0, 0, 0, len(exp)
while index < length:
e = exp[index]
if e in operSet:
# calc according to the prior
topOperator = stackOfOperator.pop()
compare = precede(topOperator, e)
if compare == '>':
try:
b = stackOfNum.pop()
a = stackOfNum.pop()
except:
return "FORMAT ERROR"
ans = operate(a, b, topOperator)
if ans == "VALUE ERROR":
return ans
else:
stackOfNum.append(ans)
elif compare == '<':
stackOfOperator.append(topOperator)
stackOfOperator.append(e)
index += 1
elif compare == '=':
index += 1
elif compare == ' ':
return "FORMAT ERROR"
else:
# get the next num
pos = index
while not exp[index] in operSet:
index += 1
temp = exp[pos:index] # delete all 0 of float in the end
last = index - 1
if '.' in temp:
while exp[last] == '':
last -= 1
temp = exp[pos:last + 1] try:
temp = Decimal(temp)
except:
return "INPUT ERROR"
stackOfNum.append(temp) if len(stackOfNum) == 1 and stackOfOperator == []:
return stackOfNum.pop()
else:
return "INPUT ERROR" if __name__ == "__main__":
# get the exp
exp = argv[1] # set the precision
getcontext().prec = 10 # delete blanks
exp = delBlank(exp) # calc and print the ans
ans = calc(exp)
print(ans)
其中需要稍微注意的细节有:
1. 表达式处理前,前后都插入一个 '#' 作为一个特殊的运算符,这样做是为了方便统一处理,即不用再去特别判断表达式是否已经结束(从而引发一系列边界问题导致代码冗长复杂,这种处理也可称之为 “哨兵” 技巧)。如果最后两个运算符相遇则说明表达式处理完毕,这个运算符的优先级也是最低的(在 prior 表中也有体现)。
2. 输出要求比较复杂,抛去错误信息输出不说(只能具体情况具体分析),不能输出多余的0,折腾了一会儿最后发现用高精度的 Decimal 可以完美满足题目要求。
3. 由于不能输出多余的0,所以在带有小数部分的数字 “录入” 时(代码115-132行),就要把一些多余的0提前去掉(代码121-126行),比如 2.0 这样的情况。
用Python3实现表达式求值的更多相关文章
- 表达式求值(noip2015等价表达式)
题目大意 给一个含字母a的表达式,求n个选项中表达式跟一开始那个等价的有哪些 做法 模拟一个多项式显然难以实现那么我们高兴的找一些素数代入表达式,再随便找一个素数做模表达式求值优先级表 - ( ) + ...
- 数据结构算法C语言实现(八)--- 3.2栈的应用举例:迷宫求解与表达式求值
一.简介 迷宫求解:类似图的DFS.具体的算法思路可以参考书上的50.51页,不过书上只说了粗略的算法,实现起来还是有很多细节需要注意.大多数只是给了个抽象的名字,甚至参数类型,返回值也没说的很清楚, ...
- nyoj305_表达式求值
表达式求值 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近又学会了一些简单的函数求值,比如,它知道函数min ...
- 利用栈实现算术表达式求值(Java语言描述)
利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...
- 数据结构--栈的应用(表达式求值 nyoj 35)
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=35 题目: 表达式求值 时间限制:3000 ms | 内存限制:65535 KB描述 AC ...
- NOIP2013普及组 T2 表达式求值
OJ地址:洛谷P1981 CODEVS 3292 正常写法是用栈 #include<iostream> #include<algorithm> #include<cmat ...
- HNU 12817 Shipura(表达式求值)
题目链接:http://acm.hnu.cn/online/?action=problem&type=show&id=12817 解题报告:定义两种运算符号,一种是>>,就 ...
- NOIP201302表达式求值
NOIP201302表达式求值 题目描述 Description 给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值. 输入描述 Input Description 输入仅有一行,为需要你计 ...
- OpenJudge计算概论-简单算术表达式求值
/*===================================== 简单算术表达式求值 总时间限制: 1000ms 内存限制: 65536kB 描述 2位正整数的简单算术运算(只考虑整数运 ...
随机推荐
- js判断是手机还是电脑访问网站
js判断是手机还是电脑访问网站 <script type="text/javascript"> <!- ...
- markdown语法书
因为初用markdown,所以对它的语法还不是很熟悉.喜欢简书的风格,特地拷贝了一份markdown语法手册,可以实现效果立显. http://www.jianshu.com/writer#/note ...
- Android开发--Adapter的应用
1.简介 Adapter的作用是为GridView,ListView等界面控件与数据之间搭建桥梁,每当列表里的每一项显示到页面时,都会调用到Adapter的getView方法 返回一个View.在An ...
- repcache实现memcached主从
1.repcached介绍 repcached是日本人开发的实现memcached复制功能,它是一个单 master单 slave的方案,但它的 master/slave都是可读写的,而且可以相互同步 ...
- SqlServer索引使用及维护
在应用系统中,尤其在联机事物处理系统中,对数据查询及处理速度已成为衡量应用系统的标准. 而采用索引来加快数据处理速度也成为广大数据库用户所接受的优化方法. 在良好的数据库设计基础上,能够有效地索引是S ...
- μC/OS-Ⅲ系统的资源管理
一.各种资源管理方法简介 μC/OS-Ⅲ系统中提供了一些基本方法用于管理共享资源(典型的共享资源有:变量.数据结构体.RAM中的表格.IO设备中的寄存器等).资源共享方法名称及适用范围如下表所示. 资 ...
- Ubuntu 修改hosts
Ubuntu系统的Hosts只需修改/etc/hosts文件,在目录中还有一个hosts.conf文件,刚开始还以为只需要修改这个就可以了,结果发现是需要修改hosts.修改完之后要重启网络.具体过程 ...
- JavaScript中的作用域
很多(JavaScript)开发者都在讨论"作用域",但它是什么?它们在JavaScript中的任何地方!我发现很多年轻的开发者不知道作用域是什么.他们中大多数人可以用jQuery ...
- 使用 VS2005 通过按钮自动上传文件到Linux
首先去官网下载 winscp,官网地址:http://winscp.net/eng/download.php 因为我这里是做自动上传工具,所以我只下载了精简版的:Portable executable ...
- hive问题整理(待续)
本人对hadoop生态系统的环境搭建.配置相关再熟悉不过了,周末刚测试过oozie相关的 今早使用hive,报错: Exception in thread "main" java. ...