今天去测试眼压,谁知道一年级带括号的口算题数值是什么

本站已经通过实名认证,所有内容由李长安大夫本人发表
测眼压时不顺利测了多次后立即测眼轴,这样得出眼轴的数值...
状态:就诊前
希望提供的帮助:
测眼压时不顺利测了多次后立即测眼轴,这样得出眼轴的数值准确吗
用药情况:
服用说明:低阿0.02Qn滴眼
是非接触测的吗?
状态:就诊前
非接触的,但气压很大,测后有流泪现象
测眼轴用什么测?lanstar还是iol master
状态:就诊前
一般是准确的。不会有误差的
李长安大夫通知通知:今天是爱眼日,感谢您的信任和支持,我将继续尽我所能为您解答疑问、提供帮助。祝愿每一位朋友健康快乐!
大夫郑重提醒:因不能面诊患者,无法全面了解病情,以上建议仅供参考,具体诊疗请一定到医院在医生指导下进行!
疾病名称:斜视&&
希望得到的帮助:女儿现在两岁半,刚发现一只眼内斜视,家里的医生让等等在治疗,坐不住啊!
病情描述:女儿斜视已发现两个月,一直眼内歇,有时正常,时好时坏,起初以为是坏毛病养成,日子一天一天过去,孩子右眼内斜越加严重,我该怎么办,孩子眼睛是否存在远视或弱视,真是着急?
疾病名称:近视&&
希望得到的帮助:孩子13岁,视力下降严重,在养好用眼习惯的前提下,如何控制视力稳定。
病情描述:右眼―5.0散光70.8 左眼-4.75散光71,咨询一下大夫戴角膜塑形镜效果好,还是视力美离焦矫正镜好。
疾病名称:儿童近视&&
希望得到的帮助:请医生给我一些治疗上的建议
病情描述:医生,我小孩2008年12月出生,今年上小学一年级,今年2月检查发现右眼75度 左眼25度 8月20复查右眼近视175 散光100 左眼100 散光150 感觉度数上升很快想咨询一下适合带OK镜吗?
疾病名称:7周岁,远视500多度,散光,戴镜两年&&
希望得到的帮助:通过治疗能拿下来眼镜吗
病情描述:远视500多度,有散光,医生说天生瞳距短
疾病名称:散光,视力在4.6到4.7&&
希望得到的帮助:需要进一步检查什么?怎么治疗?佩戴眼镜吗?有没有错过治疗时期?
病情描述:幼儿园体检时候说散光150左右,才引起重视
疾病名称:小孩眼睛近视,是否可以纠正&&
希望得到的帮助:可否纠正视力
病情描述:近视100多度,视野差,是否可以纠正视力
疾病名称:视力只有4.8/4.9&&
希望得到的帮助:如何治疗5岁宝宝视力4.8
病情描述:在家会看电视,看电视时间有点长,1小时吧
疾病名称:眼球震颤&&
希望得到的帮助:多大适合带她去看呢,现在带她去医院看,都说太小看不了,着急
病情描述:家族没有病史,六个月得了一次幼儿急疹,好了以后就开始头左右晃,眼睛也开始上下摆动了,九个多月头不晃了可是眼睛就没好过了,一岁感冒住了一回院,后面一个月左右眼睛也不怎么晃了,后面不知...
疾病名称:右眼近视1000度,左眼近视100度&&
希望得到的帮助:高度近视1000度怎么治疗
病情描述:小孩4岁右眼高度近视1000度,左眼100度
疾病名称:做了斜视手术头还是歪&&
希望得到的帮助:请医生给我一些治疗上的建议
病情描述:当时左眼水平斜视,右眼垂直斜视,右眼已经做了手术,说是可以治疗头歪,但是现在还是向左歪
疾病名称:三岁宝宝散光眼&&
希望得到的帮助:应该给宝宝怎么样治疗
病情描述:我女儿三岁去幼儿园体检发现散光眼170度左右。
疾病名称:外斜视力正常&&
希望得到的帮助:手术早好,晚做好,其他治疗方法呢
病情描述:女,6岁。孩子有外斜视,视力正常,经过医生看了,确定是外斜
疾病名称:散光弱视&&
希望得到的帮助:听说省立医院有一种佩戴的仪器可以矫正,想问下我的孩子适合吗?还有别的办法改善吗?...
病情描述:16年夏天带5周岁半孩子去眼科做了下检查,检查说是先天眼球不圆导致的眼睛散光弱势!检查了后医生给配的眼镜一直戴到今天,每两个月回去检查一次,已经检查了3次了!最近戴眼镜检查结果是一个眼...
疾病名称:弱视&&散光,远视&&
希望得到的帮助:就目前检查结果请医生诊断并告知如何诊治
病情描述:小孩先天性眼睑下垂,两周半做了右眼眼睑下垂手术,现在五周两个月视力左眼0.8右眼0.6,医生说远视加散光,很担心,现在诊治来得及吗,现在必须戴眼镜吗,以后好了可以不带眼镜吗
疾病名称:散光近视斜视&&
希望得到的帮助:请医生给我一些治疗上的建议,目前病情是否需要手术?,是否需要就诊?就诊前做哪些准备...
病情描述:女,9岁5个月。暑假学校检查双眼4.9,最近右眼4.2左眼4.6。到沈阳四院看查出近视,散光首次查出外斜视。医生,这种情况必须手术吗?有没有其他治疗方法呢?是先配眼镜,还是先治斜视呢?
疾病名称:远视
孩子1000度&&
希望得到的帮助:请刘主任给个建议,好吗?谢谢
病情描述:刘主任你好,今天去复诊了,只可惜没加到你的好, 后来挂了个普通号,测了视力,有一只眼睛还不如以前了,等会我把上一次去你那复诊的检查单和这一次的检查单发给你,请你帮我看一下好吗?谢谢
疾病名称:先天性近视&&
希望得到的帮助:5岁先天性近视可恢复吗?
病情描述:5岁左眼正常右眼在天门人民医院初诊为先天性近视600度伴有弱视,本人想带小孩来武汉协合医院复诊一下,听说检查时要点散瞳药水,请问点完散瞳药水多长时间可以检查视力,谢谢!
疾病名称:弱视散光&&
希望得到的帮助:礼拜一可以去就诊吗?
病情描述:小孩两岁检查出弱视散光,佩戴眼镜到现在。已经一年没有去检查了
疾病名称:弱视,说是闪光,目前是600℃&&
希望得到的帮助:希望给建议,如何治疗
病情描述:目前两眼无神,看什么都斜视,弱视达到600℃
疾病名称:斜视&&
希望得到的帮助:去就诊,但不手术
病情描述:有时候看某些事物眼睛闭着看。她睁开看感觉就有两个事物出现
投诉类型:
投诉说明:(200个汉字以内)
李长安大夫的信息
眼表疾病,验光,白内障,妊娠高血压眼底病变,糖尿病视网膜眼底病变及眼科健康咨询。
温州医科大学眼视光专业。
本网上诊所为朋友提供疾病咨询及相关眼科知识的咨询。
在上海复旦大学附属眼耳鼻...
李长安大夫的电话咨询
90%当天通话,沟通充分!
眼科可通话专家
副主任医师
上海市五官科医院
副主任医师
北京协和医院
副主任医师
兰州市第一人民医院
副主任医师
吉林省人民医院
副主任医师
上海第九人民医院
副主任医师
北京同仁医院眼压低会有什么样的后果?
眼压低会有什么样的后果?
眼压低会有什么样的后果?
医院出诊医生
擅长:眼底病
擅长:眼科疾病
提问者采纳
因不能面诊,医生的建议及药品推荐仅供参考
职称:主治医师
专长:代谢类疾病
&&已帮助用户:70547
指导意见:正常的眼压是维持眼睛内容物稳定以及视力的必要因素,而眼压的维持又是受泪液分泌以及回流速度和程度的影响。所以,针对眼压低的情况,一定要找到病因,并且对因治疗才对。如果不解决引起眼压低的原因,眼睛的功能就会受到影响的。
追问:有什么后果啊
回复:视力受损。
追问:会不会失明啊
追问:不去医院如何提高眼压
回复:严重的视力受损就是失明。
追问:那有什么方法提高眼压吗?
回复:首先要搞清楚眼压低的原因。才可能找到提高眼压的方法。
追问:泪液多呢?……
回复:泪液多有可能是泪鼻管堵塞引起的。
问为什么会眼压低,后果是什么
职称:医师
专长:心血管内科、消化内科、呼吸内科
&&已帮助用户:272877
您好,眼压偏低一些并没有什么关系的,有没有什么症状呢。
问眼压为8会引起什么后果?
职称:主治医师
专长:五官科,尤其擅长近视等疾病
&&已帮助用户:22030
问题分析:正常眼压是10--21mmHg,目前眼外伤之后导致一眼眼压降低,且双眼眼压差值大于8mmhg,属于异常情况,应考虑可能有睫状体损伤、房角后退等情况。意见建议:应及时到医院眼科进行全面详细检查:视力、视野、房角镜、UBM等等,根据检查结果综合分析判断具体情况,再决定下一步治疗方案。
问白内障手术后眼压过低
职称:医师
专长:内科、尤其擅长、上呼吸道感染、
&&已帮助用户:375
病情分析: 白内障手术后眼压高,建议去医院眼科做裂隙灯检查、测眼压,查出引起眼压高的原因,对症治疗意见建议:降低眼压的药物很多,缩小瞳孔,毛果芸香碱滴眼,噻吗洛尔眼压水。降低眼内压醋氮酰胺口服。高渗剂:常用50%甘油和20%甘露醇静脉快速滴注等,必要时,需要手术治疗。
职称:医师
专长:高血压病、冠心病、心律失常、心力衰竭、高脂血症、糖尿病、失眠抑郁等。
&&已帮助用户:25519
根据你描述的情况是眼科的问题这个的话你可以去医院治疗的,同时注意多喝点水
问眼压高的后果
职称:医师
专长:月经不调,痛经,细菌性阴道炎
&&已帮助用户:153493
病情分析:一般眼压高属于青光眼症状
意见建议:最好去正规眼科医院做眼压检查,假如是青光眼症状,必须手术
问视网膜脱落取哇油后眼压低怎么办
职称:医生会员
专长:高血压、糖尿病、心血管疾病
&&已帮助用户:5299
病情分析: 你好,眼压的维持需要房水的产生和排出平衡,青光眼的病人是产生过多或排出过少,引起眼压高造成的,手术的目的就是在眼球上开个新的出口,使眼压在原来的基础上进一步降低,由于个体差异,手术后出现低眼压是常见问题意见建议:术后眼压维持也要根据原来的眼压决定,长期低于10mmHg还是不好的。处理低眼压的办法有很多,要根据具体情况决定,还是看医生吧,隔个三五天看一次都是可以的
关注此问题的人还看了
大家都在搜:
眼压相关标签
免费向百万名医生提问
填写症状 描述信息,如:小孩头不发烧,手脚冰凉,是怎么回事?
无需注册,10分钟内回答
百度联盟推广
百度联盟推广
搜狗联盟推广
同类科室问答排行x
日累计回答人
日累计回答人
评价成功!苹果/安卓/wp
积分 435, 距离下一级还需 15 积分
权限: 自定义头衔, 签名中使用图片
道具: 彩虹炫, 涂鸦板, 雷达卡, 热点灯, 金钱卡, 显身卡, 匿名卡下一级可获得
道具: 抢沙发
购买后可立即获得
权限: 隐身
道具: 金钱卡, 彩虹炫, 雷达卡, 热点灯, 涂鸦板
比如有两行数,第二行的数值是在括号里面的(如下)
1 2 3 4 5 6
(0.01) (0.02) (0.03) (0.04) (0.05) (0.06)
当我把这个从文本文件copy-paste 到Excel 的时候(Text Import Wizard),有括号的数字总是被转化成负数。如果有很多这样的数据的时候,手动去纠正会很费事。请问有什么简便的方法解决呢?
载入中......
可以试试先查找替换这些括号为其他符号比如星号
controlpanel
好像不行啊,一旦替换回去就又变成负数了 :(
你用paste special -& value,就应该可以了。
本帖最后由 controlpanel 于
13:28 编辑 sqzdk 发表于
controlpanel
好像不行啊,一旦替换回去就又变成负数了 :(
12:39:04 上传
试试自定义格式吧
&nbsp&nbsp|
&nbsp&nbsp|
&nbsp&nbsp|
&nbsp&nbsp|
&nbsp&nbsp|
&nbsp&nbsp|
如有投资本站或合作意向,请联系(010-);
邮箱:service@pinggu.org
投诉或不良信息处理:(010-)
论坛法律顾问:王进律师6474人阅读
算法实践(11)
C/C++(13)
  本文乃Siliphen原创,转载请注明出处:http://blog.csdn.net/stevenkylelee
● 为什么想做一个表达式求值的程序
  最近有一个需求,策划想设置游戏关卡的某些数值,这个数值不是一个常量,而是根据关卡的某些环境数据套上一个计算表达式算出来的。这个需求无法用excel拖表预计算出,因为关卡的环境数据只有在游戏中才能产生,在excel制表时,这些都是未知的。作为程序员,我可以把计算表达式硬编码在代码中,但这个做法有缺陷,如果策划要修改计算表达式的话,只能通过我修改程序并重新编译的方式来解决。一个好的做法是,策划可以修改计算表达式,但尽量不要修改代码重新编译程序。要做到这点的话,问题就变成了:对字符串形式的表达式求值。在excel中的关卡表相应的数值单元格中,填写一个表达式,程序在运行时解释出这个表达式的值。策划要修改计算的话,就修改excel表中相应的表达式字符串即可,不用修改程序。
  在游戏的制作过程中,遇到表达式求值的问题,可以通过脚本来解决,比如:lua,js等,借助这些脚本语言的解析器来计算。目前我没在项目中用过脚本,所以,我决定做一个表达式求值的程序。其实表达式求值几年前学习数据结构时就做过了。现在再做一遍当复习,同时也把我的思考过程和大家分享一下,给没做过表达式求值的同学一些帮助。:)
  本文的实现环境是VS2013,C++语言。
  本文记录了我做表达式求值的实践过程,
  从最简单的,
  像“1+2”这样的表达式,
  像“10 + 2.55 * ( ( 10 * (
2.1 &+ 2 ) + 1.1 ) / 10”这样的表达式,
  的实践过程。
  本文的工程源代码下载:
●&计算不带括号的只有加减的表达式
  先定义一下术语。像“232”,“653”这样的数字,称作操作数。像“+“,“-”,“×”,“÷”这样的运算符号,称作操作符。操作符会连接左右2个操作数进行运算。操作符左边的操作数称为左操作数,或者操作数1。操作符右边的操作数称为右操作数,或者操作数2。括号也算是一种操作符。
  表达式由无限个操作数和无限个操作符按照规则组成。运算优先级:乘法优先于加减计算,括号优先于乘法计算,括号可以无限嵌套。
  一口气吃成个胖子,不科学。所以,一开始先考虑最简单的情况,比如:只有加减,没有乘除,没有括号,有无限个运算数和无限个加减运算符组成的表达式。先尝试实现一个相对简单的情况的计算,完成后,再考虑更复杂的情况,这样一步步循环渐进,是学习和做工程的最佳实践。
  观察一下最简单的表达式,例如“12&+&85&-&34”。
  程序一般会从左边向右边扫描这个表达式字符串,当读到第一个操作数12的1时,是在读取操作数,当读到第一个操作符“+”时,加法左边的操作数读取完毕。这时,虽然知道是要执行加法,但因为还缺少操作符右边的一个操作数,所以,暂时还不能执行计算。当加法操作符右边的操作数读取完毕时才能执行加法运算。如何判断“+”右边的操作数读取完毕呢,是在读到第二个运算符“-”时,读到“-”就意味着之前的“+”的右操作数已经结束,全部读完了。
  可以设计一个算法。用3个变量,分别记录操作数1,操作数2和运算符。一个循环从左向右读取字符。当读到第一个操作符时,意味着操作数1已经读取完毕,记录下读到的这个操作符,设置往后读的是操作数2的内容。当读到第二个操作符时,意味着操作数2已经读取完毕,这时满足了之前运算符的计算条件:已经有了操作数1,运算符,操作符2。进行计算,并且把计算的结果赋予操作数1变量。从此后,操作数1变量就永远只是存放计算结果了,只有操作数2是从表达式中读取的,不断循环反复,最终计算出表达式的值。
  用“12&+&85&-&34”这个表达式来模拟一下上面说的算法。读到“+”时,操作数1变量保存了12。记录下“+”这个操作符。当读到“-”时,操作数2变量保存了85,计算12&+&85的结果,把计算结果97赋值给操作数1变量,这时,“-”操作符的左操作数就已知了,是之前加法的计算结果。我们只要再把后面的34读取完,就满足了减法的计算要求,最终计算出表达式的值。
  本小节实现代码,如下:
#include &stdafx.h&
#include &string&
#include &iostream&
// 计算2个操作数 加减 的结果。
float Calculate( float Operand1 , float Operand2 , char Operator )
float Ret = 0 ;
if ( Operator == '+' )
Ret = Operand1 + Operand2 ;
else if ( Operator == '-' )
Ret = Operand1 - Operand2 ;
// 计算 加减,不带括号的表达式
float EvaluateExpression( const string& str )
float Operand1 = 0 ; // 操作符左边的操作数
float Operand2 = 0 ; // 操作符右边的操作数
char Operator = 0 ;
for ( size_t i = 0 , size = str.size( ) ; i & ++i )
const char& ch = str[ i ] ;
if ( '0' &= ch && ch &= '9' )
if ( Operator == 0 )
{ // 操作符为空时,表示正在解析操作符左操作数
Operand1 = Operand1 * 10 + ch - '0' ;
{ // 操作符不为空,表示正在解析操作符右边的操作数
Operand2 = Operand2 * 10 + ch - '0' ;
else if ( ch == '+' || ch == '-' )
if ( Operator == 0 )
{ // 如果操作符为空,先保存操作符,当遇到下一个操作符时,才进行计算。
Operator =
{ // 如果之前存在了操作符,当前遇到第二个操作符,意味着先计算之前的 加减。
Operand1 = Calculate( Operand1 , Operand2 , Operator ) ; // 操作数1 和 操作数2 进行计算
Operand2 = 0 ; // 操作数2 置为空,为了保存下一个操作符右边的操作数。
Operator = // 保存本次遇到的操作符,当前不会计算这个操作符,因为操作符右边的操作数还没有读取到。
} // end for
// 表达式遍历完后,会遗留下最后一个操作数,进行最后一项的计算。
Operand1 = Calculate( Operand1 , Operand2 , Operator ) ;
return Operand1 ;
int _tmain(int argc, _TCHAR* argv[])
string str = &12 + 85 - 34 + 11 + 222 + 234 - 500& ;
float Ret = EvaluateExpression( str ) ;
cout && &算术表达式: & + str && & 的结果是: & && Ret &&
  以上代码运行后的结果是:
  对于表达式“12 + 85 - 34 + 11 + 222 + 234 - 500”的计算,输出结果是:30。正确!Oh yeah!
  嘿嘿,60行左右就完成了”不带加减不带括号的表达式“的计算。
●&计算不带括号的加减乘除的表达式
  OK,循环渐进,加大难度。现在再来考虑更复杂一点的情况,加入乘除运算符。乘除的优先级高于加减,在有加减乘除的混合运算中优先计算乘除。从现在开始,表达式的计算开始有了优先级的概念,我们定义,左边的加减运算符的优先级高于右边的加减运算符,左边的乘除运算符的优先级高于右边的乘除运算符,所有乘除运算符的优先级高于所有加减运算符。
  我们依旧是从左向右扫描表达式字符串。看这个表达式“5&+&2&*&3&-&4”,当扫描到运算符“*”时,我们是不能计算之前遇到的“+”运算符的,因为乘法优先级高于加法,要先计算出乘法的结果,然后把乘法结果作为加法的右运算数,加法才能计算。加法依赖于乘法的计算结果,加法的运算被延迟了,所以,在遍历表达式时,要一路保存遇到的运算数和运算符。
  上面的情况,加法依赖于乘法,那么乘法什么时候计算呢?在遇到“-”减法运算符时。减法的运算符优先级低于乘法,理所当然地要先计算乘法才能计算加法。这时,从过去的记录中,取出乘法的2个操作数和乘法运算符进行计算。计算完成后,表达式变成“5&+&6&-&4”,这时减法又和过去记录的操作符进行比较,发现加法的优先级高于减法,那么先计算5&+&6&的结果,最后计算&11&-&4的结果。
  当前遇到的运算符一定是和上一次最近遇到的运算符进行优先级比较的,上一次遇到的运算符一定是和上一次的上一次遇到的运算符进行优先级比较。而运算符的读入,是从左到右,其实这是一个栈的访问顺序,读入运算符时是运算符入栈,进行运算符优先级比较决定当前是否能够计算时,是运算符出栈。同理,运算符如此了,那么对应的操作数也是如此。
  现在,我们可以设计下基础算法了。从左向右遍历表达式,遇到运算数,先入操作数栈保存起来供以后使用。遇到运算符也入运算符栈保存起来供以后使用。
  如果,当前遍历遇到了运算符,并且运算符栈不为空的话,就和栈顶运算符,也就是最近上一次遇到的运算符进行优先级比较,如果上一次遇到的运算符优先级高,那么,拿出之前遇到的2个运算数和上一次遇到的运算符进行运算,运算结果入栈保存起来。当前遇到的运算符再和运算符栈里面的栈顶运算符进行比较,反复如此。当当前运算符的优先级大于栈里面保存的运算符时,意味着,之前遇到的运算符也不能立马计算了,把当前运算符入栈保存。继续读表达式。
  这里当前遇到的运算符有迫使之前遇到的运算符进行计算的作用(能够迫使计算的规则是当前运算符优先级低于上一次遇到的运算符),当它迫使不了的时候,就入栈保存当前运算符它自己,让下一个读入的运算符来迫使它进行计算。
  本小节实现代码,如下:
#include &string&
#include &iostream&
#include &unordered_map&
// 运算符优先级表
unordered_map& char , unordered_map& char , char & & P
// 初始化运算符优先级数据
void InitPriorities( )
Priorities[ '+' ][ '-' ] = '&' ;
Priorities[ '+' ][ '+' ] = '&' ;
Priorities[ '+' ][ '*' ] = '&' ;
Priorities[ '+' ][ '/' ] = '&' ;
Priorities[ '-' ][ '-' ] = '&' ;
Priorities[ '-' ][ '+' ] = '&' ;
Priorities[ '-' ][ '*' ] = '&' ;
Priorities[ '-' ][ '/' ] = '&' ;
Priorities[ '*' ][ '-' ] = '&' ;
Priorities[ '*' ][ '+' ] = '&' ;
Priorities[ '*' ][ '*' ] = '&' ;
Priorities[ '*' ][ '/' ] = '&' ;
Priorities[ '/' ][ '-' ] = '&' ;
Priorities[ '/' ][ '+' ] = '&' ;
Priorities[ '/' ][ '*' ] = '&' ;
Priorities[ '/' ][ '/' ] = '&' ;
// 计算2个操作数 加减乘除 的结果。
float Calculate( float Operand1 , float Operand2 , char Operator )
float Ret = 0 ;
if ( Operator == '+' )
Ret = Operand1 + Operand2 ;
else if ( Operator == '-' )
Ret = Operand1 - Operand2 ;
else if ( Operator == '*' )
Ret = Operand1 * Operand2 ;
else if ( Operator == '/' )
Ret = Operand1 / Operand2 ;
// 计算 加减乘除,不带括号的表达式
float EvaluateExpression( const string& str )
vector& float & O // 操作数栈,也可以用 stack& float &
vector& char & O // 操作符栈,也可以用 stack& char &
float OperandTemp = 0 ;
for ( size_t i = 0 , size = str.size( ) ; i & ++i )
const char& ch = str[ i ] ;
if ( '0' &= ch && ch &= '9' )
{ // 读取一个操作数
OperandTemp = OperandTemp * 10 + ch - '0' ;
else if ( ch == '+' || ch == '-' || ch == '*' || ch == '/' )
// 遇到一个操作符后,意味着之前读取的操作数已经结束。保存操作数。
Operands.push_back( OperandTemp ) ;
// 清空,为读取下一个操作符做准备。
OperandTemp = 0 ;
// 当前遇到的操作符作为操作符2,将和之前遇到的操作符(作为操作符1)进行优先级比较
const char& Opt2 =
for ( ; Operators.size( ) & 0 ; )
// 比较当前遇到的操作符和上一次遇到的操作符的优先级
const char& Opt1 = Operators.back( ) ;
char CompareRet = Priorities[ Opt1 ][ Opt2 ] ;
if ( CompareRet == '&' )
{ // 如果操作符1 大于 操作符2 那么,操作符1应该先计算
// 取出之前保存的操作数2
float Operand2 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作数1
float Operand1 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作符。当前计算这个操作符,计算完成后,消除该操作符,就没必要保存了。
Operators.pop_back( ) ;
// 二元操作符计算。并把计算结果保存。
float Ret = Calculate( Operand1 , Operand2 , Opt1 ) ;
Operands.push_back( Ret ) ;
else if ( CompareRet == '&' )
{ // 如果操作符1 小于 操作符2,说明 操作符1 和 操作符2 当前都不能进行计算,
// 退出循环,记录操作符。
} // end for
// 保存当前遇到操作符,当前操作符还缺少右操作数,要读完右操作数才能计算。
Operators.push_back( Opt2 ) ;
} // end for
上面的 for 会一面遍历表达式一面计算,如果可以计算的话。
当遍历完成后,并不代表整个表达式计算完成了。而会有2种情况:
1.剩余1个运算符。
2.剩余2个运算符,且运算符1 小于 运算符2。这种情况,在上面的遍历过程中是不能进行计算的,所以才会被遗留下来。
到这里,已经不需要进行优先级比较了。情况1和情况2,都是循环取出最后读入的操作符进行运算。
Operands.push_back( OperandTemp ) ;
for ( ; Operators.size( ) & 0 ; )
// 取出之前保存的操作数2
float Operand2 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作数1
float Operand1 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出末端一个操作符
char Opt = Operators.back( ) ;
Operators.pop_back( ) ;
// 二元操作符计算。
float Ret = Calculate( Operand1 , Operand2 , Opt ) ;
Operands.push_back( Ret ) ;
return Operands[ 0 ] ;
int _tmain( int argc , _TCHAR* argv[ ] )
// 初始化运算符的优先级
InitPriorities( ) ;
string str = &11 - 6 + 2 * 3 + 50 / 10 * 2 + 12& ;
float Ret = EvaluateExpression( str ) ;
cout && &算术表达式: & + str && & 的结果是: & && Ret &&
  以上代码运行后的结果是:
  对表达式“11 - 6 + 2 * 3 + 50 / 10 * 2 + 12”的计算,输出结果是:33 。正确!Oh yeah!
●&计算带括号的加减乘除的表达式
  OK,循环渐进,加大难度。现在,要考虑带括号的表达式。不带括号的加减乘除表达式的做法和带括号的表达式的做法是很接近的,因为括号也可以认为是一种运算符,只是括号运算符不是用来参与计算的,括号的作用是干预加减乘除计算的优先级。
&  “1 * ( 2 + 3)”这个表达式中,“(”会阻止它左边的乘法“*”立即计算。要达到阻止的目的,“(”运算符的优先级需要高于乘除,只有当前遇到操作符优先级高于上一次遇到的运算符的优先级,才能阻止之前遇到的运算符立即计算。而“)”会迫使之前遇到的运算符立即运算,要达到迫使的目的,“)”运算符要的优先级要低于加减乘除,只有之前遇到的运算符优先级高时,之前的遇到的运算符才应该进行计算。
  “)”不断迫使之前的运算符进行计算,如果,它遇到了它的镜像“(”,就是括号被消除了,“(”和”)”湮灭,就像物质遇到反物质一样。
  “(”出现的地方都会紧跟在一个运算符之后,“)”出现的地方后面都会紧跟一个运算符。括号运算符的作用是干预其他运算符的计算优先级,所以,括号不会在画面上分割2个运算数,只会紧贴运算符。这让运算数的解析和之前程序相比多了一些规则。
  现在来梳理一下带括号的表达式的计算算法:
  先定义运算符比较优先级:
  加减比较时,左边的运算符优先级高。
  乘除比较时,左边的运算符优先级高。
  乘除优先级高于加减。
  “(“左括号的优先级高于乘除,是最高优先级的运算符。
  “)”右括号的优先级低于加减,是最低优先级的运算符。
  “(”和“)”优先级相等。相等的含义是,括号被消除。
for 从左向右扫描表达式
  if( 遇到一个运算符 )
    入栈保存操作数 ;
    while( 操作符栈不为空 )
      if( 当前遇到的运算符优先级低于操作符栈栈顶运算符的优先级 )
        操作数栈弹出2个操作数 ;
        操作符栈弹出一个操作符 ;
        用弹出的操作符计算弹出的2个操作数的结果 ;
        结果入栈操作数栈 ;
      else if(当前遇到的运算符优先级高于操作符栈栈顶运算符的优先级 )
        退出循环 ;
      else if(当前遇到的运算符优先级等于操作符栈栈顶运算符的优先级 )
        )遇到(,括号消除 ;
    入栈当前遇到的运算符 ;
表达式扫描完成后,会遗留一些操作符未能计算,此时已经不存在括号了。
不断从栈中弹出操作符和操作数进行计算,直到操作符栈为空。
本节的实现代码如下:
#include &string&
#include &iostream&
#include &unordered_map&
// 运算符优先级表
unordered_map& char , unordered_map& char , char & & P
// 初始化运算符优先级定义数据
void InitPriorities( )
Priorities[ '+' ][ '-' ] = '&' ;
Priorities[ '+' ][ '+' ] = '&' ;
Priorities[ '+' ][ '*' ] = '&' ;
Priorities[ '+' ][ '/' ] = '&' ;
Priorities[ '+' ][ '(' ] = '&' ;
Priorities[ '+' ][ ')' ] = '&' ;
Priorities[ '-' ][ '-' ] = '&' ;
Priorities[ '-' ][ '+' ] = '&' ;
Priorities[ '-' ][ '*' ] = '&' ;
Priorities[ '-' ][ '/' ] = '&' ;
Priorities[ '-' ][ '(' ] = '&' ;
Priorities[ '-' ][ ')' ] = '&' ;
Priorities[ '*' ][ '-' ] = '&' ;
Priorities[ '*' ][ '+' ] = '&' ;
Priorities[ '*' ][ '*' ] = '&' ;
Priorities[ '*' ][ '/' ] = '&' ;
Priorities[ '*' ][ '(' ] = '&' ;
Priorities[ '*' ][ ')' ] = '&' ;
Priorities[ '/' ][ '-' ] = '&' ;
Priorities[ '/' ][ '+' ] = '&' ;
Priorities[ '/' ][ '*' ] = '&' ;
Priorities[ '/' ][ '/' ] = '&' ;
Priorities[ '/' ][ '(' ] = '&' ;
Priorities[ '/' ][ ')' ] = '&' ;
Priorities[ '(' ][ '+' ] = '&' ;
Priorities[ '(' ][ '-' ] = '&' ;
Priorities[ '(' ][ '*' ] = '&' ;
Priorities[ '(' ][ '/' ] = '&' ;
Priorities[ '(' ][ '(' ] = '&' ;
Priorities[ '(' ][ ')' ] = '=' ;
// 不存在操作符1是&)&和 操作符2 比较的情况
// 因为 ) 会迫使之前的操作符进行运算。
// 直到遇到匹配的“(”操作符,双双被消除掉
// 所以下面的数据无意义。
Priorities[ ')' ][ '+' ] = '?' ;
Priorities[ ')' ][ '-' ] = '?' ;
Priorities[ ')' ][ '*' ] = '?' ;
Priorities[ ')' ][ '/' ] = '?' ;
Priorities[ ')' ][ '(' ] = '?' ;
Priorities[ ')' ][ ')' ] = '?' ;
// 计算2个操作数 加减乘除 的结果。
float Calculate( float Operand1 , float Operand2 , char Operator )
float Ret = 0 ;
if ( Operator == '+' )
Ret = Operand1 + Operand2 ;
else if ( Operator == '-' )
Ret = Operand1 - Operand2 ;
else if ( Operator == '*' )
Ret = Operand1 * Operand2 ;
else if ( Operator == '/' )
Ret = Operand1 / Operand2 ;
// 计算 加减,不带括号的表达式
float EvaluateExpression( const string& str )
vector& float & O // 操作数栈,也可以用 stack& float &
vector& char & O // 操作符栈,也可以用 stack& char &
float OperandTemp = 0 ;
char LastOperator = 0 ;
// 记录最后遇到的操作符
for ( size_t i = 0 , size = str.size( ) ; i & ++i )
const char& ch = str[ i ] ;
if ( '0' &= ch && ch &= '9' )
{ // 读取一个操作数
OperandTemp = OperandTemp * 10 + ch - '0' ;
else if ( ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
ch == '(' || ch == ')' )
// 有2种情况 是没有操作数需要入栈保存的。
// 1 当前操作符是 “(”。(的左边的操作符已经负责操作数入栈了。
// 2 上一次遇到的操作符是“)”。)本身会负责操作数入栈,)后面紧跟的操作符不需要再负责操作数入栈。
if ( ch != '(' && LastOperator != ')' )
// 遇到一个操作符后,意味着之前读取的操作数已经结束。保存操作数。
Operands.push_back( OperandTemp ) ;
// 清空,为读取下一个操作符做准备。
OperandTemp = 0 ;
// 当前遇到的操作符作为操作符2,将和之前遇到的操作符(作为操作符1)进行优先级比较
const char& Opt2 =
for ( ; Operators.size( ) & 0 ; )
// 比较当前遇到的操作符和上一次遇到的操作符的优先级
const char& Opt1 = Operators.back( ) ;
char CompareRet = Priorities[ Opt1 ][ Opt2 ] ;
if ( CompareRet == '&' )
{ // 如果操作符1 大于 操作符2 那么,操作符1应该先计算
// 取出之前保存的操作数2
float Operand2 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作数1
float Operand1 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作符。当前计算这个操作符,计算完成后,消除该操作符,就没必要保存了。
Operators.pop_back( ) ;
// 二元操作符计算。并把计算结果保存。
float Ret = Calculate( Operand1 , Operand2 , Opt1 ) ;
Operands.push_back( Ret ) ;
else if ( CompareRet == '&' )
{ // 如果操作符1 小于 操作符2,说明 操作符1 和 操作符2 当前都不能进行计算,
// 退出循环,记录操作符。
else if ( CompareRet == '=' )
// 操作符相等的情况,只有操作符2是“)”,操作数1是“(”的情况,
// 弹出原先保存的操作符“(”,意味着“(”,“)”已经互相消掉,括号内容已经计算完毕
Operators.pop_back( ) ;
} // end for
// 保存当前遇到操作符,当前操作符还缺少右操作数,要读完右操作数才能计算。
if ( Opt2 != ')' )
Operators.push_back( Opt2 ) ;
LastOperator = Opt2 ;
} // end for
上面的 for 会一面遍历表达式一面计算,如果可以计算的话。
当遍历完成后,并不代表整个表达式计算完成了。而会有2种情况:
1.剩余1个运算符。
2.剩余2个运算符,且运算符1 小于 运算符2。这种情况,在上面的遍历过程中是不能进行计算的,所以才会被遗留下来。
到这里,已经不需要进行优先级比较了。情况1和情况2,都是循环取出最后读入的操作符进行运算。
if ( LastOperator != ')' )
Operands.push_back( OperandTemp ) ;
for ( ; Operators.size( ) & 0 ; )
// 取出之前保存的操作数2
float Operand2 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作数1
float Operand1 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出末端一个操作符
char Opt = Operators.back( ) ;
Operators.pop_back( ) ;
// 二元操作符计算。
float Ret = Calculate( Operand1 , Operand2 , Opt ) ;
Operands.push_back( Ret ) ;
return Operands[ 0 ] ;
int _tmain( int argc , _TCHAR* argv[ ] )
// 初始化运算符的优先级
InitPriorities( ) ;
string str = &1 + ( 1 + 2 ) * ( ( 54 - 51 ) + 8 ) / 3& ;
float Ret = EvaluateExpression( str ) ;
cout && &算术表达式: & + str && & 的结果是: & && Ret &&
  以上代码运行后的结果是:
  对表达式“1 + ( 1 + 2 ) * ( ( 54 - 51 ) + 8 ) / 3”&的计算,输出结果是:12。正确!Oh
● 表达式预处理:词法分析,分离出表达式中的词素。
  到此为止,已经做到了支持带括号的四则混合运算,看似已完成任务。实际上,还有一些问题没有解决。仔细观察以上的实现会发现,这些实现虽然用的是float型变量来保存计算结果,但对表达式中的运算数的解析实现得很简单,解析运算数的算法认为运算数的所有组成部分都是整数,这导致了虽然能计算出
5 / 2 = 2.5 但不能反过来计算 2.5 * 2 = 5。
  为了支持获取浮点型的运算数,需要修改对字符串的解析算法。如果直接在原先的代码上增加对浮点型数字的解析,会让原先的代码变得很复杂,解析的实现和运算规则的实现混在一起,最后会让整个程序变得难以维护、难以扩展、难以改变。
  考虑这样一种情况,如果要支持带有指数表示的运算数,类似:”2.55e5” , “0.1e100”等,支持表达式的表示形式越多,解析算法就会越复杂。有可能实现解析的代码量会超过实现运算规则的。
  敏捷开发的原则之一:SRP。解析表达式是一个单一的职责,运算规则的实现是一个单一的职责,两者应该分开,这样更有利于程序的维护,扩展和复用。编译器的实现也正是这样,对表达式的解析对应词法分析,对运算规则的实现可以对应到语法分析。
  因为,我们之前的主要精力是放在运算规则的实现上,所以在对解析要求不高的情况下,我把解析和运算实现混在了一起。现在,为了支持带小数表示运算数,我们先做一个简单的词法分析器。
  针对表达式的词法分析器,具备如下功能:
输入表达式字符串“23.11 * ( 22e2 + 10 )”,输出每个词法单元。词法单元是在表达式中有完整含义的最小单位。比如:“23.11”是一个完整的运算数词素,“*”是一个完整的运算符词素。词法分析器对这个表达式会输出 & 23.11 & , & * & , & ( & , & 22e2 & , & + & , & 10 & , & ) & 词素序列。
  OK,现在来实现我们的简单词法分析器。因为本人水平和时间有限,就用状态机粗略做下。用状态机做有一个好处,可以发现表达式中的某些语法错误。比如,像“2.1.1”这样的连续的2个小数点的运算数,可以发现其语法有错且知道错在哪里。但词法分析阶段能发现的错误仅限于词法,各种词素组合在一起形成的句子,这条句子有没有语法和语义上的错误,需要到语法分析时才能被发现。
  本小节的实现代码如下:
作者:Siliphen
#include &string&
#include &vector&
#include &iostream&
#include &unordered_map&
// 词法单元结构体
struct Token
enum class Type
// 数字常量
Operator ,
Type TokenT
Token( Type TokenType = Type::Num , const string& Value = && )
this-&TokenType = TokenT
this-&Value = V
bool IsDigit( const char& ch )
return '0' &= ch && ch &= '9' ? true :
bool IsOperator( const char& ch )
return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')' ? true :
bool IsBlank( const char& ch )
return ch == ' ' || ch == '\t' ? true :
// 分离出表达式中所有的词素
string GetTokens( const string& str , vector& Token && Tokens )
string Ret = &OK& ;
Token theT
int State = 0 ;
for ( size_t i = 0 ; i & str.size( ) ; ++i )
const char& ch = str[ i ] ;
switch ( State )
if ( IsDigit( ch ) )
theToken.TokenType = Token::Type::N
theToken.Value.clear( ) ;
theToken.Value.push_back( ch ) ;
State = 1 ;
case 1: // 数字的整数部分
if ( IsDigit( ch ) )
theToken.Value.push_back( ch ) ;
else if ( ch == '.' )
theToken.Value.push_back( ch ) ;
State = 2 ;
else if ( IsOperator( ch ) )
// 获得一个操作数
Tokens.push_back( theToken ) ;
theToken.Value.clear( ) ;
// 读到操作符的第一个字符
theToken.TokenType = Token::Type::O
theToken.Value.push_back( ch ) ;
State = 3 ;
else if ( ch == 'E' || ch == 'e' )
theToken.Value.push_back( ch ) ;
State = 4 ;
else if ( IsBlank( ch ) )
return string( &表达式操作数整数部分出现非法字符:& ) +
case 2: // 数字的小数部分
if ( IsDigit( ch ) )
theToken.Value.push_back( ch ) ;
else if ( IsOperator( ch ) )
// 获得一个操作数
Tokens.push_back( theToken ) ;
theToken.Value.clear( ) ;
// 读到操作符的第一个字符
theToken.TokenType = Token::Type::O
theToken.Value.push_back( ch ) ;
State = 3 ;
else if ( ch == 'E' || ch == 'e' )
theToken.Value.push_back( ch ) ;
State = 4 ;
else if ( IsBlank(ch) )
return string( &表达式操作数小数部分出现非法字符:& ) +
case 4: // 数字的指数部分
if ( IsDigit( ch ) )
theToken.Value.push_back( ch ) ;
else if ( IsOperator( ch ) )
// 获得一个操作数
Tokens.push_back( theToken ) ;
theToken.Value.clear( ) ;
// 读到操作符的第一个字符
theToken.TokenType = Token::Type::O
theToken.Value.push_back( ch ) ;
State = 3 ;
else if ( IsBlank( ch ) )
return string( &表达式操作数指数部分出现非法字符:& ) +
case 3: // 操作符状态
if ( IsDigit( ch ) )
// 获得一个操作数
Tokens.push_back( theToken ) ;
theToken.Value.clear( ) ;
// 读到操作数的第一个字符
theToken.TokenType = Token::Type::N
theToken.Value.push_back( ch ) ;
State = 1 ;
else if ( IsOperator( ch ) )
// 获得一个操作符
Tokens.push_back( theToken ) ;
theToken.Value.clear( ) ;
// 读到操作符的第一个字符
theToken.TokenType = Token::Type::O
theToken.Value.push_back( ch ) ;
else if ( IsBlank( ch ) )
return string( &表达式操作符部分出现非法字符:& ) +
} // end for
Tokens.push_back( theToken ) ;
// 对表达式进行测试
void TestExpr( const string& str )
vector& Token & L
string Ret = GetTokens( str , Lexemes ) ;
cout && &表达式:& && str && &分离词素的结果是:& &&
cout && Ret &&
// 打印词法分析的结果
unordered_map& Token::Type , string & NameM
NameMapping[ Token::Type::Num ] = &操作数& ;
NameMapping[ Token::Type::Operator ] = &操作符& ;
for ( auto& theLexeme : Lexemes )
cout && NameMapping[ theLexeme.TokenType ] && &
& && theLexeme.Value &&
int _tmain(int argc, _TCHAR* argv[])
vector& string & ExprList =
11e2 + 2.6 / ( 23.1E5 + 22 )
11e2 + 2.6a & ,
&2.2 + 1.1.1& ,
for ( auto& str : ExprList )
TestExpr( str ) ;
cout &&&——————————————————————————————————&&&
  以上代码运行后的结果是:
  我写了一个测试函数,用来测试对不同的表达式输出的结果,验证实现是否正确。OK,看起来没问题,现在我们的对运算规则的实现,再也不用关心对表达式的解析咯,Oh,Yeah!
● 增加对带小数的运算数的支持
  之前实现的词法分析器,为了更好地复用,先把以上代码封装成一个LexicalAnalyser类。这个类的作用就是用来对表达式进行分词的。
  把上节代码整理成如下形式:
  同时,表达式求值的算法也整理到一个叫ExpressionEvaluator的类中,如下图:
  重构表达式求值的算法,把原先直接对表达式字符串的处理改成对词法单元的处理。
  重构的计算函数代码如下:
std::string ExpressionEvaluator::Evaluate( const vector& Token && Tokens , float& ExprRet )
string strRet = &OK& ;
vector& float & O // 操作数栈,也可以用 stack& float &
vector& char & O // 操作符栈,也可以用 stack& char &
for ( size_t i = 0 , size = Tokens.size( ) ; i & ++i )
const Token& theToken = Tokens[ i ] ;
if ( theToken.TokenType == Token::Type::Num )
Operands.push_back( atof( theToken.Value.c_str() ) )
else if ( theToken.TokenType == Token::Type::Operator )
// 当前遇到的操作符作为操作符2,将和之前遇到的操作符(作为操作符1)进行优先级比较
const char& Opt2 = theToken.Value[ 0 ] ;
for ( ; Operators.size( ) & 0 ; )
// 比较当前遇到的操作符和上一次遇到的操作符的优先级
const char& Opt1 = Operators.back( ) ;
char CompareRet = m_Priorities[ Opt1 ][ Opt2 ] ;
if ( CompareRet == '&' )
{ // 如果操作符1 大于 操作符2 那么,操作符1应该先计算
// 取出之前保存的操作数2
float Operand2 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作数1
float Operand1 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作符。即将搞定这个操作符的计算。
Operators.pop_back( ) ;
// 二元操作符计算。并把计算结果保存。
float Ret = Calculate( Operand1 , Operand2 , Opt1 ) ;
Operands.push_back( Ret ) ;
else if ( CompareRet == '&' )
{ // 如果操作符1 小于 操作符2,说明 操作符1 和 操作符2 当前都不能进行计算,
// 退出循环,记录操作符。
else if ( CompareRet == '=' )
// 操作符相等的情况,只有操作符2是“)”,操作数1是“(”的情况,
// 弹出原先保存的操作符“(”,意味着“(”,“)”已经互相消掉,括号内容已经计算完毕
Operators.pop_back( ) ;
} // end for
// 保存当前遇到操作符,当前操作符还缺少右操作数,要读完右操作数才能计算。
// 只有“)”操作符不要被保存
if ( Opt2 != ')' )
Operators.push_back( Opt2 ) ;
} // end for
上面的 for 会一面遍历表达式一面计算,如果可以计算的话。
当遍历完成后,并不代表整个表达式计算完成了。而会有2种情况:
1.剩余1个运算符。
2.剩余2个运算符,且运算符1 小于 运算符2。这种情况,在上面的遍历过程中是不能进行计算的,所以才会被遗留下来。
到这里,已经不需要进行优先级比较了。情况1和情况2,都是循环取出最后读入的操作符进行运算。
for ( ; Operators.size( ) & 0 ; )
// 取出之前保存的操作数2
float Operand2 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出之前保存的操作数1
float Operand1 = Operands.back( ) ;
Operands.pop_back( ) ;
// 取出末端一个操作符
char Opt = Operators.back( ) ;
Operators.pop_back( ) ;
// 二元操作符计算。
float Ret = Calculate( Operand1 , Operand2 , Opt ) ;
Operands.push_back( Ret ) ;
} // end for
ExprRet = Operands[ 0 ] ;
return strR
  可以看到,重构后的表达式求值算法变得更短(不到100行),更容易理解,更强(支持对运算数是浮点数的运算)。
  写一个测试来测试以上算法的正确性。
  测试代码如下:
#include &ExpressionEvaluator.h&
#include &iostream&
// 测试表达式求值的正确性
void TestExprEval( const string& Expr , float ExpectedRet )
float ExprRet = 0
ExpressionEvaluator ExprE
string strRet = ExprEval.Evaluate( Expr , ExprRet ) ;
if ( strRet != &OK& )
cout && &表达式计算出错,信息:& && strRet&&
cout && &表达式:& + Expr &&
cout && &计算结果是:& && ExprRet&&
if ( fabs( ExpectedRet - ExprRet ) & 0.00001 )
cout && &与预期相等,计算正确。& &&
cout && &与预期不等,计算错误!预期结果:& && ExpectedRet &&
int _tmain(int argc, _TCHAR* argv[])
typedef pair& string , float & I
vector& Item & TestList =
Item( &2.1 * ( 2.11 + 3.5557 )& , 11.89797 ) ,
Item( &3 * ( 2.4 + 3 ) + 1.1& , 17.3 ) ,
Item( &3 * ( 10 + 3 ) + ( 1.1 - 0.1) * 2& , 41 ) ,
Item( &1.4 - 0.4 + 2 & , 3 ) ,
Item( &10 + 10 / ( ( 1 - 2 ) * 4 + 2 )& , 5 ) ,
Item( &( 2 + 11.5 ) * 20& , 270 ) ,
Item( &(( 2 + 5 ) / 7 )& , 1 ) ,
Item( &((((33 - 66 / 3 ))))& , 11 ) ,
Item( &10 * ( ( 2 + 1 ) * ( 3 + 4 ) + 1 ) - ( 50 - ( 23 - 20 / 10 ) ) / 2& , 205.5 ) ,
Item( &0& , 0 ) ,
for ( auto& it : TestList )
TestExprEval( it.first , it.second ) ;
  以上代码运行后的结果是:
  OK,通过了全部正确性验证测试!Oh,yeah!
●&总结:  
  表达式求值的问题,到此结束了么?当然没!以上实现的表达式求值还不完善,比如:
  1.还不支持带符号的运算数的计算。对于带符号的运算数会认为是运算符,导致计算出错。处理运算数的正负号,可以放在词法分析阶段处理,也可以放到语法分析阶段,我觉得放在后者中可能更合适。
  2.不支持指数形式的运算数的运算,C语言标准库的atof函数不能对像“23.2e5”这种形式的浮点数表示进行正确转换。我们需要写自己的转换函数。
  表达式求值的问题没结束,但我得先结束了。关于正负号的处理,逆波兰式的生成与计算,表达式树的生成与计算等实践,下回再搞吧。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:208103次
积分:2309
积分:2309
排名:第16212名
原创:25篇
评论:347条
(1)(1)(1)(1)(2)(1)(4)(1)(4)(1)(3)(2)(3)

我要回帖

更多关于 王者荣耀 括号数值 的文章

 

随机推荐