前段时间我们制作了一个表达式求值的程序,
我们赋予他基本框架与功能:【实例】表达式求值——利用stack实现五种运算符的运算 | 《课程设计》笔记 (i-nmb.cn)
以及更新了一目运算符的支持:【实例更新】表达式求值——加入新的单目运算符 | 《课程设计》笔记 (i-nmb.cn)
问题引入
我们在经过这两个步骤之后,我们可以基本实现 表达式的求值操作 ,但是L 2 8 =没有括号就显得十分奇怪,并且我们在输入其他字符时报错方法还可以加以优化,让程序能够定位错误。
所以目前我们需要解决的问题有两种,即:
1.加入括号进行运算
2.优化报错方法
一、加入括号
首先我们要进行加入括号的运算方法
问题思考
1.在前面的单目运算符以及双目运算符中,我们需要进行数的运算,那么加入括号,操作数又有什么变化?
2.括号的特点是什么?他的左右运算符有没有什么特点?
3.插入括号后,具体的算法(或者说 运算流程 )是怎么样子的?
4.如何“优雅地”添加括号并且不让程序做很大改动?
这些问题我们需要思考与解决,以下为思考解决的部分过程
加入括号,操作数又有什么变化?
无论是单目运算符或者是双目运算符,其本质为运算符,是处理数的一种符号,只是运算过程中在处理 操作数的数目 的多少而划分。
但是括号不是数学中的运算符号,他并没有处理操作数,没有改变操作数的数目和数值,他只是单纯的改变运算符之间的优先级、只是运算顺序的辅助符号。添加括号时 在栈顶的操作数 就在栈顶,没有任何变化。
括号的特点是什么?
1.因为括号不是数学中的运算符号,括号最直接的特点就是,加入左括号时,不改变在左括号(之前的其他符号的运算顺序,并且在右括号)之前,都正常运算(即括号内的正常运算),左括号需要等待右括号才进行出栈
2.由1可以推断,无论左括号前面是什么,直接进栈;左括号后面的符号只要不是右括号,左括号始终不出栈,右括号始终不进栈。
具体的算法(或者说 运算流程 )
比如3*(2+4)=
首先等号入栈[history.top()]
第一步,3入栈 operands
第二步, * 与history.top()(=)优先级比较, * 入栈。
第三步,由上述问题,无论左括号前面是什么,直接进栈,所以(入栈
第四步,2入栈
第五步,左括号与+进行优先级比较,由上述括号的特点可知:左括号后面的符号只要不是右括号,左括号始终不出栈,所以开始计算
第六步,输入4入栈,两次取出栈顶的元素(4和2)并进行+运算,得数为6,压入 operands栈顶,弹出 + 号
第七步,输入右括号,与栈顶history.top()的左括号匹配,所以弹出左括号,并且右括号始终不进栈。
第八步,输入等号,等号优先级最小,先运算现在栈顶history.top()的 * 号,连续从栈顶取出两个元素相乘,得数18,弹出 * 号
第九步,此时栈顶[history.top()]为“=”,与输入的等号相等,程序结束,输出operands.top()【此时为得数】
如何“优雅地”添加括号?
根据以上括号的特点及运算流程
我们可以知道:
1.无论左括号前面是什么,直接进栈;
2.左括号后面的符号只要不是右括号,左括号始终不出栈,并且将符号压入栈
3.当等到右括号,则开始计算【等到右括号,“()”内的数计算完毕】
4.当等到右括号,则弹出左括号
换成伪代码的形式
将以上四点转换成伪代码的形式:
if(op=='('){push(op)}top=='('&&op!=')':push(op)if(op==')'):calctop=='('&&op==')':pop('(')
将伪代码转换为现实
以上的伪代码只是我们的“梦想”,要让其成为现实的道路并不容易。
因为我们之前的程序大体以及框架已经确定,不能重新打乱和改变。
那么我们如何“优雅地”添加括号并且不让程序做很大改动?
答案是把括号作为一种特殊的“运算符”加入map
我们需要用以下特性:
1.无论左括号前面是什么,直接进栈
2.只要不是右括号,左括号始终不出栈
3.右括号始终不进栈
得出以下结论
1.(左括号的右优先级要非常大)
2.(左括号的左优先级要非常小)
3.(右括号的右优先级非常小)
转为代码
1 | std::map<oper, prv_num_t> p_num={ |
我们重新审查代码,使用以上代码,可以实现以下目标:
1.无论左括号前面是什么,直接进栈;if(op=='('){push(op)}
2.左括号后面的符号只要不是右括号,左括号始终不出栈,并且将符号压入栈 top=='('&&op!=')':push(op)
3.当等到右括号,则开始计算 if(op==')'):calc【等到右括号,“()”内的数计算完毕】
但是还有最后一点没有实现
即:4.当等到右括号,则弹出左括号top=='('&&op==')':pop('(')
此时我们必须修改源代码,在之前的判断等号与哨兵相等之前(if(prior(history.top(),op)==0) break;)根据伪代码if(top=='('&&op==')'):pop('(')加入以下代码,
1 | if(op==")"){ |
因为左括号的左优先数==右括号的右优先数,如果经过if(prior(history.top(),op)==0) break;那就跳出循环,运算就出错了,所以我们应该在此之前加入代码即可
1 | while(1) |
此时我们基本实现了加入括号的问题了
二、优化报错方法
解决了上面的问题,在输入右括号而不输入左括号,或者输入其他不识别的运算符时,报错代码来自
这样不利于我们定位出错的位置,所以我们需要优化程序:优化报错方法
若我们输入其他错误字符时,若需要报错,则需要正在通过map函数查找字符对应的优先数的函数中int prior(oper op1, oper op2)进行
在其中加入以下代码
1 | int prior(oper op1, oper op2) |
若我们在没有左括号的情况下输入右括号,报错,则需要在字符压入栈之前判断
1 | operand solve() |
三、问题解决完毕
目前我们需要解决的问题有两种,即:
1.加入括号进行运算
2.优化报错方法
而这两种在上面已经基本上解决了,至此完结~(撒花)
下面附上更改后的代码
1 |
|
Hexo博客NJK主题的相关文章智能推荐插件hexo-article-recommender进阶配置(DIY)
DIY客制化文章底部“文章推荐模块”,本篇文章介绍Nunjucks(NJK)模板的hexo主题(如NexT主题)如何实现。hexo-article-recomm
Hexo博客EJS主题的相关文章智能推荐插件hexo-article-recommender进阶配置(DIY)
DIY客制化文章底部“文章推荐模块”,本篇文章介绍EJS模板的hexo主题如何实现。hexo-article-recommender为Hexo博客提供本地化智能
--- over ---
- 本文链接: https://i-nmb.cn/example-optimization-2.html
- 版权声明: 本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。