Разъяснение по std :: cin - PullRequest
       2

Разъяснение по std :: cin

1 голос
/ 01 августа 2011

Это взято прямо из "языка программирования C ++" Бьярна Страуструпа. Я просто хотел бы получить пояснения о том, как он накапливает цифры в переменной (int number_value). Пожалуйста, не копируйте код, я его не написал (полный код из главы 6 внизу поста).

В частности, когда анализатор вызывает лексер, как лексер создает число с помощью cin. Я верю, что ответ в этих восьми строках, но я хотел бы объяснить, как это работает.

if( isalpha( ch ) ) {
    (*input).putback( ch );
    (*input) >> string_value;
    return curr_tok=NAME;
} else {
    error( "bad token " );
    return curr_tok=PRINT;
}

Мне кажется, что при первом вызове get_token () он помещает полный список expression_list в cin или любой другой входной поток, на который указывает вход (внутри get_token ()).

(*input) >> ch;

Я знаю, что ch объявлен как символ, но что произойдет, если вы введете 123.4 + 5.432; (предполагая, что input - cin) cin теперь содержит «строку» 123.4 + 5.432, содержащуюся в его потоке. Затем мы переходим к оператору switch в лексере (get_token ()). Я предполагаю::

ch == 1?

на данный момент? Далее внутри оператора switch мы «провалимся» на «.» дело. Здесь мы помещаем '1' обратно в поток и записываем его в number_value?

(*input).putback( ch );
(*input) >> number_value;

Теперь number_value = 1, и мы вернемся к парсеру. Так как мы нашли NUMBER, он снова вызывает get_token (). И оператор cin << вызывается снова. Разве следующий вызов (* input) >> number_value не поместит 2 в числовое значение с перезаписью 1 (при условии, что на входе все еще 123.4 + 5.432)? Что здесь происходит. Думаю, мне нужно лучше понять, как работают потоки. Если бы кто-то мог найти время, дать краткое объяснение и указать мне хороший ресурс, я был бы очень признателен.

Спасибо,

Мэтью Хогган

Для тех, у кого нет книги, код:

#include <iostream>
#include <stdlib.h>
#include <string>
#include <sstream>
#include <map>
#include <cctype>

std::istream *input;

double number_value;
int no_of_errors;
std::string string_value; 
std::map<std::string,double> table;

enum Token_value {
    NAME,        NUMBER,        END,
    PLUS='+',    MINUS='-',     MUL='*',       DIV='/',  
    PRINT=';',   ASSIGN='=',    LP='(',        RP=')'
}; 

Token_value curr_tok=PRINT;

double expr( bool );
double term( bool );
double prim( bool );
Token_value get_token( );
double error( std::string s ) {
    no_of_errors++;
    std::cerr << "error: " << s << std::endl;
    return 1.0;
}

Token_value get_token( ) {
    char ch = 0;
    (*input) >> ch;

    switch( ch ) {
        case 0: {
            return curr_tok=END;
        }
        case ';': 
        case '*':
        case '/':
        case '+':
        case '-':
        case '(':
        case ')':
        case '=': {
            return curr_tok = static_cast<Token_value>( ch );
        }
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '.': {
            (*input).putback( ch );
            (*input) >> number_value;
            return curr_tok=NUMBER;
        }
        default: {
            if( isalpha( ch ) ) {
                (*input).putback( ch );
                (*input) >> string_value;
                return curr_tok=NAME;
            } else {
                error( "bad token " );
                return curr_tok=PRINT;
            }
        }
    }
}

int main( int argc, char *argv[ ] ) {
    switch( argc ) {
        case 1: {
            input = &std::cin;
            break;
        }
        case 2: {
            input = new std::istringstream( argv[1] );
            break;
        }
        default: {
            error(" To many arguments" );
            return 1;
        }
    }
    table["pi"] = 3.1415926535897932385;
    table["e"] = 2.7182818284590452354;

    while( (*input) ) {
        get_token( );
        if( curr_tok == END ) {
            break;
        }
        if( curr_tok == PRINT ) {
            continue;
        }
        std::cout << expr( false ) << std::endl;
    }

    if( input != &std::cin ) {
        delete input;
    }

    return 0;
}

double expr( bool get ) {
    double left = term( get );

    for( ; ; ) {
        switch( curr_tok ) {
            case PLUS: {
                left += term( true );
                break;
            }
            case MINUS: {
                left -= term( true );
                break;
            }
            default: {
                return left;
            }
        }
    }
}

double term( bool get ) { 
    double left = prim( get );
    for( ; ; ) {
        switch( curr_tok ) {
            case MUL: {
                left *= prim( true );
                break;
            }
            case DIV: {
                if( double d = prim( true ) ) {
                    left /= d;
                    break;                   
                }
                else {
                    return error( "divide by 0" );
                }
            }
            default: {
                return left;
            }
        }
    }
}

double prim( bool get ) {
    if( get ) {
        get_token( );
    }

    switch( curr_tok ) {
        case NUMBER: {
            double v = number_value;
            get_token( );
            return v;
        }
        case NAME: {
             double &v = table[string_value];
             if( get_token( ) == ASSIGN ) {
                 v = expr( true );
                 return v;
             }
        }
        case MINUS: {
            return -prim( true );
        }
        case LP: {
            double e = expr( true );
            if( curr_tok != RP ) {
                return error( "')' expected" );
            }
            get_token( );
            return e;
        }
        default: {
            return error( "primary expected" );
        }
    }
}

Ответы [ 2 ]

2 голосов
/ 01 августа 2011

Трюк вызван разным поведением следующих трех строк:

char ch;                   std::cin >> ch;
std::string string_value;  std::cin >> string_value;
double number_value;       std::cin >> number_value;

Первая получает только один символ, вторая и третья получают несколько символов длясоздайте переменную правильного типа.

Строки перегружают глобальную функцию operator>>, чтобы предоставить версию для строк, и эта версия использует пробел в качестве разделителя (если вам нужно вводить пробелы с вашей строкой, вы должнызагляните в getline).

Двойная версия использует функцию-член istream& operator>> (double& val); и будет читать символы только тогда, когда они имеют смысл при формировании двойного значения.

Итак, допустим, вы вводитеabc.Код cin >> ch заполнит ch символом 'a', удалив его из входного потока.Затем вы обнаружите это с помощью isapha в случае по умолчанию, поскольку оно не соответствует ни одному из других случаев.

В этот момент вы нажимаете этот символ 'a' обратно во входной поток, чтобы вы могли повторнопрочитайте его и выполните cin >> string_value, который получит всю строку abc, , а не один символ.

Аналогично, если вы введете 3.14159, он будет перехвачен case '3' проверьте, символ будет возвращен во входной поток, и cin >> number_value получит все значение.

2 голосов
/ 01 августа 2011

Теперь number_value = 1, и мы вернемся к парсеру.

Нет. (*input) >> number_value; читает целое число double, то есть 123.4, поскольку число_значение имеет тип double. Кроме этого, вы правы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...