简体   繁体   English

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

[英]chapter 6 practice and principles using c++ [drill] using tokens (calculator)

I have come at a stand still on chapter 6 of Bjarne Stroustrup's book Practice and Principles using c++. 我在Bjarne Stroustrup的《使用c ++的实践和原理》一书的第6章停了下来。 Basically, I have read this chapter twice and tried to understand the examples about grammars and tokens related to making a calculator. 基本上,我已经阅读了本章两次,试图理解与制作计算器有关的语法和标记的示例。 I would say that I understand the examples but when it all comes together, I am not able to debug the code and get the faulty program Bjarne gives us working. 我想说我理解了示例,但是当所有示例组合在一起时,我将无法调试代码并获得Bjarne使我们无法正常工作的程序。

We had to solve 5 errors that causes the program to not compile, and 3 logic errors. 我们必须解决5个导致程序无法编译的错误,以及3个逻辑错误。 I solved 3 of the compile errors, but left 2 of them because I'm not sure if the way I fix them actually helps break the program. 我解决了3个编译错误,但留下了2个,​​因为我不确定我修复它们的方式是否真的有助于破坏程序。 I will mark these with 我会用

//Syntax error "reason" //语法错误“原因”

on the code. 在代码上。 After that, there will be 3 logic errors and I would appreciate any help on figuring these out. 在那之后,将出现3个逻辑错误,希望能帮助您解决这些错误。 You don't have to give me the actual answer for the logic ones, but some clues at least would be appreciated, probably even more so than answers. 您不必给我逻辑上的实际答案,但是至少可以理解一些线索,可能比答案更重要。 But if you give me the answer, that would also be great. 但是,如果您给我答案,那也很好。

I keep looking online to see if anyone else has posted questions about the drill, but so far I haven't come across any. 我一直在网上寻找是否有人发表了有关演习的问题,但到目前为止我还没有遇到任何问题。 All help is appreciated. 感谢所有帮助。 Thanks. 谢谢。

This chapter has been giving me a head ache and I would really like to get past it! 本章让我头痛不已,我真的很想过去!

Added information: 补充信息:

  1. What I did was declare full to be bool and true and make buffer and char. 我所做的就是声明full为bool和true,并创建buffer和char。 This is located in the get() function. 它位于get()函数中。

  2. in the main function, I declared val a double. 在主函数中,我声明val为double。

  3. After this, the program will tell me that get() function and primary() function don't return on all paths, so I made the default for get() return a token and the default for primary() return ts.value. 在此之后,程序将告诉我get()函数和primary()函数不会在所有路径上返回,因此我将get()的默认值设为返回令牌,而primary()的默认值设为ts.value。

After this, the compiler shows no errors but won't run. 此后,编译器不会显示任何错误,但是不会运行。

The code below has none of these changes, because I believe my changed help break the program. 下面的代码没有这些更改,因为我相信更改后的帮助会破坏程序。

// 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;
}

//------------------------------------------------------------------------------

As you know, the purpose of the programme is to compute simple arithmetic expressions. 如您所知,该程序的目的是计算简单的算术表达式。 The expressions are made of numbers, arithmetic operators (sum, substraction, multiplication, division, remainder), and parentheses to group compounds and override the usual operator precedences. 表达式由数字,算术运算符(总和,减法,乘法,除法,余数)和括号组成,以对化合物进行分组并覆盖通常的运算符优先级。

The programme is built on top of the Token and Token_stream classes, which permit the lexical analysis of the original expression encoded as a stream of characters (the text input by the user). 该程序建立在TokenToken_stream类的顶部,这些类允许对编码为字符流(用户输入的文本)的原始表达式进行词法分析。 The later class extracts possibly meaningful characters from the underlying stream ( cin ), and depending on their values, build Token instances, tokens with additional semantic (*): 后面的类从基础流( cin )中提取可能有意义的字符,并根据其值构建Token实例,具有附加语义(*)的令牌:

  • parentheses and operators carry their usual meaning 括号和运算符具有通常的含义
  • digits indicate the beginning of numbers, thus groups of digits are extracted as double 数字表示数字的开头,因此将数字组提取为double

Since the Token class uses a single character to identify its kind , numbers are associated with the character 8 ; 由于Token类使用一个字符来标识其类型 ,因此数字与字符8相关联; note, however, that this is completely arbitrary, any other character not used by another token kind (ie. operators or parentheses here) could be used in its place: char kind might as well be int kind , or better yet enum kind with a well defined enum to represent that value without incidence on the programme (in this simple setting, char happens to work well and is easy to implement). 但是请注意,这完全是任意的,其他标记类型未使用的任何其他字符(即此处的运算符或括号)都可以代替它使用: char kind最好是int kind ,或者更好的是用a enum kind定义明确的enum以表示该值而不会在程序中产生任何影响(在这种简单的设置中, char可以很好地工作并且易于实现)。

The code performing the actual expression computation is divided in 3 functions, naturally covering the operator precedence: 执行实际表达式计算的代码分为3个函数,自然涵盖了运算符的优先级:

  • primary computes number from constants (ie. numbers as they come out of the stream), and parenthetized expressions, primary根据常量(即,从流中出来的数字)和带括号的表达式来计算数字,
  • term computes operations with higher precedence, term计算优先级更高的运算,
  • expression deals with lower precedence operations expression处理优先级较低的运算

Again, in each case, a double is returned, clearly indicating the type used to represent numbers. 同样,在每种情况下,都将返回一个double精度值,以明确指示用于表示数字的类型。

The syntax error: 语法错误:

As explained by the other answer, the val variable must be declared in the scope of its use. 如另一个答案所述,必须在使用范围内声明val变量。 You may define that variable either as a double (the type of the computation result), or a string (in which case the value will be converted). 您可以将该变量定义为double (计算结果的类型)或string (在这种情况下,将转换值)。

The logic errors: 逻辑错误:

The first one is located in the number token construction code: look at the characters used to detect if a number is coming next in the stream. 第一个位于数字令牌构造代码中:查看用于检测流中下一个数字是否存在的字符。

The second one is in the Token_stream::get class method: roughly speaking, its declaration and definition don't match. 第二个是在Token_stream::get类方法中:大致来说,它的声明和定义不匹配。

The third one looks like a copy and paste error in the expression function: it looks like 2 operations are handled, but do we really compute 2 different output? 第三个看起来像是expression函数中的复制和粘贴错误:看起来像处理了2个操作,但是我们真的计算出2个不同的输出吗?


(*): Given that >> skips blanks and other "unneeded" character from the stream, we really have two layers of tokening. (*):假设>>从流中跳过空格和其他“不需要的”字符,我们实际上有两层标记。

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

The compiler does not know what val is all about. 编译器不知道val的全部含义。 You should declare it on top and assign it to a string you wanted to display. 您应该在顶部声明它,并将其分配给要显示的字符串。

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

The compilation errors are: 编译错误为:

1) In the first line after #include 1)在#include之后的第一行

lass Token  //Wrong
class Token //Correct

2) In the member function get() is declared outside the class definition so the notation is wrong, this generate the error of full and buffer variables 2)在成员函数get()的声明是在类定义之外声明的,因此表示法是错误的,这会生成完整和缓冲变量的错误

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

3) In the primary member function 3)在主要成员函数中

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

4) In the expression member function 4)在表达式成员函数中

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

5) In the main function the variable val is not declare so you need: 5)在主函数中未声明变量val,因此您需要:

double val=0;

With that corrections the program should be able to compile correctly: 经过这些更正,程序应该能够正确编译:

The Logic errors are fun to hunt so the clues are: 寻找逻辑错误很有趣,因此线索如下:

  1. Try using the number 8 尝试使用数字8
  2. Use multiplications 使用乘法
  3. Use Subtractions 使用减法

If you use Visual C++, try to use breakpoints and debug using F11, is easier to find the logic errors 如果使用Visual C ++,请尝试使用断点并使用F11进行调试,这样更容易发现逻辑错误

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用C ++的编程原理和实践第4章练习1 - Programming Principles and Practice using c++ chapter 4 drill 1 使用C ++的编程原理和实践第4章钻取,第7步 - Programming Principles and Practice Using C++ Chapter 4 Drill, Step 7 在“编程:使用C ++的原理和实践”(第4章)的第7章中使用“ cin”获得7号钻的不同结果 - Getting different results using “cin” for Drill #7 in Chapter 4 of Programming: Principles and Practice using C++ (Stroustrup) 编程:原理与实践使用C ++第4章钻取第6步:关于数值范围的一般问题 - Programming: Principles and Practice Using C++ chapter 4 drill step 6 : General question about numeric range “使用C ++的原理和实践”章节6.3.1代码错误? - “Principles and Practice Using C++” chapter 6.3.1 code error? 不理解 Bjarne Stroustrup 的第 6 章“编程:使用 C++ 的原理和实践”中的这部分代码 - Don't understand this part of code from "Programming: principles and practice using C++", chapter 6 by Bjarne Stroustrup 编程-使用C ++的原理和实践-第3章尝试本练习-重复单词检测 - Programming - Principles and Practice using C++ - Chapter 3 Try this exercise - Repeated word detection 编程原理与实践:第4章演练第1部分 - Programming Principles and Practice: chapter 4 drill part 1 使用C ++循环钻取的编程原理和实践。 找不到退出循环的方法 - Programming Principles and Practice using C++ while-loop drill. Can't find a way to exit the loop 在编程:使用 C++ 的原则和实践的第 4 章进行练习。 比亚恩·斯特劳斯楚普 - working on Drills from chapter 4 of Programming: Principles and Practice using C++. Bjarne Stroustrup
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM