第6章使用c ++ [drill]使用令牌(计算器)的实践和原则

时间:2013-06-06 09:33:14

标签: c++

我在Bjarne Stroustrup的“使用c ++实践和原则”一书的第6章中站了起来。基本上,我已经两次阅读本章,并试图理解与制作计算器相关的语法和标记的例子。我会说我理解这些例子但是当它们全部结合在一起时,我无法调试代码并获得Bjarne给我们工作的错误程序。

我们必须解决导致程序无法编译的5个错误,以及3个逻辑错误。我解决了3个编译错误,但剩下2个,因为我不确定我修复它们的方式是否真的有助于破坏程序。我将用

标记这些

//语法错误“reason”

代码。在那之后,将有3个逻辑错误,我将不胜感激任何有关解决这些问题的帮助。你不必给我逻辑上的实际答案,但至少有一些线索会被赞赏,甚至可能比答案更重要。但如果你给我答案,那也会很棒。

我一直在网上查看是否有其他人发布了关于演习的问题,但到目前为止我还没有遇到任何问题。所有帮助表示赞赏。谢谢。

本章给了我一个头疼,我真的想过去了!

添加信息:

  1. 我所做的是宣布完全是bool和true并制作缓冲区和char。它位于get()函数中。

  2. 在main函数中,我声明val为double。

  3. 在此之后,程序将告诉我get()函数和primary()函数不会在所有路径上返回,因此我使get()的默认值返回一个标记,并且默认为primary( )return ts.value。

  4. 在此之后,编译器显示没有错误但不会运行。

    下面的代码没有这些变化,因为我相信我改变了帮助会破坏程序。

    // The code 
    
    
    #include "../../../std_lib_facilities.h"
    
    //------------------------------------------------------------------------------
    
    class Token {
    public:
    char kind;        // what kind of token
    double value;     // for numbers: a value 
    Token(char ch)    // make a Token from a char
        :kind(ch), value(0) { }    
    Token(char ch, double val)     // make a Token from a char and a double
        :kind(ch), value(val) { }
    };
    
    //------------------------------------------------------------------------------
    
    class Token_stream {
    public: 
    Token_stream();   // make a Token_stream that reads from cin
    Token get();      // get a Token (get() is defined elsewhere)
    void putback(Token t);    // put a Token back
    private:
    bool full;        // is there a Token in the buffer?
    Token buffer;     // here is where we keep a Token put back using putback()
    };
    
    //------------------------------------------------------------------------------
    
    // The constructor just sets full to indicate that the buffer is empty:
    Token_stream::Token_stream()
    :full(false), buffer(0)    // no Token in buffer
    {
    }
    
    //------------------------------------------------------------------------------
    
    // The putback() member function puts its argument back into the Token_stream's buffer:
    void Token_stream::putback(Token t)
    {
    if (full) error("putback() into a full buffer");
    buffer = t;       // copy t to buffer
    full = true;      // buffer is now full
    }
    
    //------------------------------------------------------------------------------
    
    Token get()
    {
    if (full) {       // do we already have a Token ready?  //Syntax error "full" and "buffer" not declared
        // remove token from buffer
        full=false;
        return buffer;
    } 
    
    char ch;
    cin >> ch;    // note that >> skips whitespace (space, newline, tab, etc.)
    
    switch (ch) {
    case ';':    // for "print"
    case 'q':    // for "quit"
    case '(': case ')': case '+': case '-': case '*': case '/': 
        return Token(ch);        // let each character represent itself
    case '.':
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '9':
        {    
            cin.putback(ch);         // put digit back into the input stream
            double val;
            cin >> val;              // read a floating-point number
            return Token('8',val);   // let '8' represent "a number"
        }
      default:
        error("Bad token");
      }
    }
    
    //------------------------------------------------------------------------------
    
    Token_stream ts;        // provides get() and putback() 
    
    //------------------------------------------------------------------------------
    
    double expression();    // declaration so that primary() can call expression()
    
    //------------------------------------------------------------------------------
    
    // deal with numbers and parentheses
    double primary()
    {
    Token t = ts.get();
    switch (t.kind) {
    case '(':    // handle '(' expression ')'
        {    
            double d = expression();
            t = ts.get();
            if (t.kind != ')') error("')' expected)");
            return d;
        }
    case '8':            // we use '8' to represent a number
        return t.value;  // return the number's value
    default:
        error("primary expected");
     }
    }
    
    //------------------------------------------------------------------------------
    
    // deal with *, /, and %
    double term()
    {
    double left = primary();
    Token t = ts.get();        // get the next token from token stream
    
    while(true) {
        switch (t.kind) {
        case '*':
            left *= primary();
            t = ts.get();
        case '/':
            {    
                double d = primary();
                if (d == 0) error("divide by zero");
                left /= d; 
                t = ts.get();
                break;
            }
        default: 
            ts.putback(t);     // put t back into the token stream
            return left;
        }
      }
    }
    
    //------------------------------------------------------------------------------
    
    // deal with + and -
    double expression()
    {
    double left = term();      // read and evaluate a Term
    Token t = ts.get();        // get the next token from token stream
    
    while(true) {    
        switch(t.kind) {
        case '+':
            left += term();    // evaluate Term and add
            t = ts.get();
            break;
        case '-':
            left += term();    // evaluate Term and subtract
            t = ts.get();
            break;
        default: 
            ts.putback(t);     // put t back into the token stream
            return left;       // finally: no more + or -: return the answer
        }
     }
    }
    
    //------------------------------------------------------------------------------
    
    int main()
    try
    {
    while (cin) {
        Token t = ts.get();
    
        if (t.kind == 'q') break; // 'q' for quit
        if (t.kind == ';')        // ';' for "print now"
            cout << "=" << val << '\n'; //Syntax error "val" not declared
        else
            ts.putback(t);
        val = expression(); //Syntax error "val" not declared
    }
    keep_window_open();
    }
    catch (exception& e) {
        cerr << "error: " << e.what() << '\n'; 
        keep_window_open();
        return 1;
    }
    catch (...) {
        cerr << "Oops: unknown exception!\n"; 
        keep_window_open();
        return 2;
    }
    
    //------------------------------------------------------------------------------
    

3 个答案:

答案 0 :(得分:6)

如您所知,该程序的目的是计算简单的算术表达式。表达式由数字,算术运算符(求和,减法,乘法,除法,余数)和括号组成,用于对化合物进行分组并覆盖通常的运算符优先级。

该程序建立在TokenToken_stream类之上,允许对原始表达式进行词法分析,编码为字符流(用户输入的文本)。后面的类从底层流(cin)中提取可能有意义的字符,并根据它们的值构建Token个实例,带有附加语义的标记(*):

  • 括号和运算符具有通常含义
  • 数字表示数字的开头,因此数字组被提取为double

由于Token类使用单个字符来标识其种类,因此数字与字符8相关联;但请注意,这完全是任意的,其他任何未使用的其他字符(即运算符或括号)都可以在其位置使用:char kind也可能是int kind,或者更好的是enum kind,定义明确enum来代表该值,而不会在程序上发生(在这个简单的设置中,char恰好运行良好且易于实现)。

执行实际表达式计算的代码分为3个函数,自然覆盖运算符优先级:

  • primary计算常量中的数字(即从流中出来的数字)和括号表达式,
  • term计算具有更高优先级的操作,
  • expression处理较低优先级操作

同样,在每种情况下,返回double,清楚地表明用于表示数字的类型。

语法错误:

正如另一个答案所解释的那样,val变量必须在其使用范围内声明。您可以将该变量定义为double(计算结果的类型)或string(在这种情况下,值将被转换)。

逻辑错误:

第一个位于数字令牌构造代码中:查看用于检测流中是否有下一个数字的字符。

第二个是Token_stream::get类方法:粗略地说,它的声明和定义不匹配。

第三个看起来像expression函数中的复制和粘贴错误:它看起来像处理了2个操作,但我们真的计算了2个不同的输出吗?


(*):鉴于>>从流中跳过空白和其他“不需要的”字符,我们确实有两层令牌。

答案 1 :(得分:1)

if (t.kind == ';')        // ';' for "print now"
    cout << "=" << val << '\n'; //Syntax error "val" not declared
else
    ts.putback(t);
val = expression(); //Syntax error "val" not declared

编译器不知道val的全部内容。您应该在顶部声明它并将其分配给您想要显示的字符串。

String val = '';
...
val = 'message';

答案 2 :(得分:1)

编译错误是:

1)在#include

之后的第一行
lass Token  //Wrong
class Token //Correct

2)在成员函数中,get()在类定义之外声明,因此符号错误,这会生成full和buffer变量的错误

Token get()                 //Wrong
Token Token_stream::get()   //Correct

3)在主要成员函数中

 if (t.kind != ')') error("')' expected);   //Wrong
 if (t.kind != ')') error("')' expected");  //Correct

4)在表达式成员函数

double left = term(;    //Wrong
double left = term();   //Correct

5)在main函数中,变量val未声明,因此您需要:

double val=0;

通过这些更正,程序应该能够正确编译:

逻辑错误很有趣,所以线索是:

  1. 尝试使用数字8
  2. 使用乘法
  3. 使用减法
  4. 如果使用Visual C ++,尝试使用断点并使用F11调试,更容易找到逻辑错误