当前位置:   article > 正文

《C++ Primer》第9章 9.6节习题答案_c++primer练习9.6

c++primer练习9.6

《C++ Primer》第9章 顺序容器

9.6节容器适配器 习题答案

练习9.52:使用stack处理括号化的表达式。当你看到一个左括号,将其记录下来。当你在一个左括号之后看到一个右括号,从stack中pop对象,直至遇到左括号,将左括号也一起弹出栈。然后将一个值(括号内的运算结果)push到栈中,表示一个括号化的(子)表达式已经处理完毕,被其运算结果所替代。

【出题思路】

本题作为栈数据结构的基本练习有些复杂。原因在于,表达式的解析算法对于编程初学者来说相对复杂。解答本题更多的工作是设计表达式解析的逻辑,栈的操作变为次要地位。

【解答】

如下所示,本题的解答已经较为复杂,但这已是将问题大幅简化之后的解答了。我们假定表达式中只有加、减两种运算,由于所有运算优先级相同,无须进行复杂的优先级判断。由于加、减运算都是左结合,因此,遇到一个数值,直接与之前的运算数进行它们中间的运算即可。唯一的例外是括号,它将改变计算次序——括号里的运算将先于括号前的运算执行。不过,我们可以将它看作“阻断”了括号前的表达式,将括号内的部分看作一个独立的表达式优先处理,计算结果作为一个运算数参与之前的运算即可。

表达式解析的逻辑大致如下:

1.读入了一个运算数v。

a)若栈空或栈顶是左括号,则v是第一个运算数,直接压栈即可。

b)否则,v前必须是一个运算符,再之前是另一个运算数v’,从栈顶弹出这两项,将计算结果压栈即可;否则,就抛出一个“缺少运算符”异常。

2.读入了一个左括号,直接压栈。

3.读入了一个运算符,

a)若栈空或栈顶不是一个运算数,则抛出一个“缺少运算数”异常。注意,若运算符之前是一个右括号,之前也已处理完毕,栈顶是其计算结果,仍应该是运算数,不影响此逻辑。

b)否则,运算符压栈。

4.读入了一个右括号,

a)若栈空,表明之前没有与之配对的左括号,抛出“未匹配右括号”异常。

b)若栈顶是左括号,表明括号对之间无表达式,抛出“空括号”异常。

c)若栈顶不是运算数,表明括号内缺少一个运算数,抛出一个异常。

d)弹出此运算数v,若栈空或栈顶不是左括号,仍抛出“未匹配右括号”异常;否则弹出左括号,把v作为新运算数,执行1中的逻辑。

5.以上均不是,则出现了非法输入,会在转换为数值时产生异常。

6.当字符串处理完毕后,判断栈中是否有且仅有一个运算数,若是,此值即为表达式运算结果,输出它;否则,表达式非法。

值得注意的是,为了在栈中保存括号、运算符和运算数三类对象,程序中定义了枚举类型obj_type。栈中每个对象都保存了类型t和数值v(如果t是VAL的话)。

  1. #include <iostream>
  2. #include <string>
  3. #include <deque>
  4. #include <stack>
  5. #include <stdexcept>
  6. using namespace std;
  7. //表示栈中对象的不同类型
  8. enum obj_type {LP, RP, ADD, SUB, VAL};
  9. struct obj{
  10. obj(obj_type type, double val = 0)
  11. {
  12. t = type;
  13. v = val;
  14. }
  15. obj_type t;
  16. double v;
  17. };
  18. inline void skipws(string &exp, size_t &p)
  19. {
  20. p = exp.find_first_not_of(" ", p);
  21. }
  22. inline void new_val(stack<obj> &so, double v)
  23. {
  24. if(so.empty() || so.top().t == LP)//空栈或左括号
  25. {
  26. so.push(obj(VAL, v));
  27. cout << "push " << v << endl;
  28. }
  29. else if(so.top().t == ADD || so.top().t == SUB)
  30. {
  31. //之前是运算符
  32. obj_type type = so.top().t;
  33. so.pop();
  34. // if(type == ADD)
  35. // cout << "pop + " << endl;
  36. // else
  37. // cout << "pop - " << endl;
  38. // cout << "pop " << so.top().v << endl;
  39. //执行加减法
  40. if(type == ADD)
  41. v += so.top().v;
  42. else
  43. v = so.top().v - v;
  44. so.pop();
  45. so.push(obj(VAL, v));//运乍算结果压栈
  46. //cout << "push " << v << endl;
  47. }
  48. else
  49. throw invalid_argument("缺少运算符");
  50. }
  51. int main()
  52. {
  53. stack<obj> so;
  54. string exp;
  55. size_t p = 0, q;
  56. double v;
  57. cout << "请输入表达式: ";
  58. getline(cin, exp);
  59. while(p < exp.size())
  60. {
  61. skipws(exp, p);//跳过空格
  62. if(exp[p] == '(')
  63. {
  64. so.push(obj(LP));
  65. p++;
  66. cout << "push LP" << endl;
  67. }
  68. else if(exp[p] == '+' || exp[p] == '-')
  69. {
  70. //新运算符
  71. if(so.empty() || so.top().t != VAL)
  72. //空栈或之前不是运算符
  73. throw invalid_argument("缺少运算数");
  74. if(exp[p] == '+')//运算符压栈
  75. so.push(obj(ADD));
  76. else
  77. so.push(obj(SUB));
  78. ++p;
  79. //cout << "push " << exp[p - 1] << endl;
  80. }
  81. else if(exp[p] == ')')//右括号
  82. {
  83. p++;
  84. if(so.empty())//之前无配对的左括号
  85. throw invalid_argument("未匹配右括号");
  86. if(so.top().t == LP)//一对括号之间无内容
  87. throw invalid_argument("空括号");
  88. if(so.top().t == VAL)//正确:括号内运算结果
  89. {
  90. v = so.top().v;
  91. so.pop();
  92. //cout << "pop " << v << endl;
  93. if(so.empty() || so.top().t != LP)
  94. throw invalid_argument("未匹配右括号");
  95. so.pop();
  96. //cout << "pop LP" << endl;
  97. new_val(so, v);//与新运算数逻辑一致
  98. }
  99. else
  100. throw invalid_argument("缺少运算数");
  101. }
  102. else//应该是运算数
  103. {
  104. v = stod(exp.substr(p), &q);
  105. p += q;
  106. new_val(so, v);
  107. }
  108. }
  109. if(so.size() != 1 || so.top().t != VAL)
  110. throw invalid_argument("非法表达式");
  111. cout << so.top().v << endl;
  112. return 0;
  113. }

运行结果:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/178246
推荐阅读
相关标签
  

闽ICP备14008679号