читать несколько строк, но особенно ... разбирать их эффективно - PullRequest
1 голос
/ 16 октября 2011

Мне нужно прочитать несколько строк с конкретными ключевыми словами в начале. У меня есть основная проблема, и мне нужна помощь, чтобы помочь мне.

Вот тип ввода:

ключевое слово1 0,0 0,0
Ключевое слово1 1,0 5,0
Ключевое слово2 10,0
Ключевое слово3 0,5
Ключевое слово4 6,0

Правила:

  • строки, содержащие ключевое слово1 и ключевое слово2, ДОЛЖНЫ быть в этом порядке И перед любыми другими строками.

  • строки, содержащие ключевое слово3 и ключевое слово4, могут быть в любом порядке

  • ключевое слово1 ДОЛЖНО сопровождаться двумя двойными

  • ключевое слово2, 3 и 4 ДОЛЖНЫ сопровождаться 1 двойным

  • в конце блока строк, содержащего все четыре ключевых слова, за которыми следует их двойное значение, разрывы «цикла» и запуск вычисления.

Вот мой источник:

using namespace std;

int main (int argc, const char * argv[]) {    
    vector<double> arrayInputs;
    string line;
    double keyword1_first, keyword1_second, keyword4, 
          keyword3, keyword2;
    bool inside_keyword1=false, after_keyword2=false, 
          keyword4_defined=false, keyword3_defined=false ;

//cin.ignore();

 while (getline(cin, line)) {
     if (inside_keyword1 && after_keyword2 && keyword3 && keyword4) {
         break;
     }
     else
     {
         std::istringstream split(line);
         std::vector<std::string> tokens;
         char split_char = ' ';
         for (std::string each; std::getline(split, each, split_char); tokens.push_back(each));

         if (tokens.size() > 2)
         {
             if (tokens[0] != "keyword1") return EXIT_FAILURE; // input format error
             else
             {
                 keyword1_first = atof(tokens[1].c_str());
                 keyword1_second = atof(tokens[2].c_str());

                 inside_keyword1 = true;
             }
         }
         else
         {
             if (tokens[0] == "keyword2")
             {
                 if (inside_keyword1)
                 {
                     keyword2 = atof(tokens[1].c_str());
                     after_keyword2 = true;
                 }

                 else return EXIT_FAILURE; // cannot define anything else keyword2 after keyword1 definition

             }
             else if (tokens[0] == "keyword3")
             {
                 if (inside_keyword1 && after_keyword2)
                 {
                     keyword3 = atof(tokens[1].c_str());
                     keyword3_defined  = true;
                 }
                 else return EXIT_FAILURE; // cannot define keyword3 outside a keyword1
             }
             else if (tokens[0] == "keyword4")
             {
                 if (inside_keyword1 && after_keyword2)
                 {
                     keyword4 = atof(tokens[1].c_str());
                     keyword4_defined  = true;
                 }
                 else return EXIT_FAILURE; // cannot define keyword4 outside a keyword1
             }
         }
     }
 }

 // Calculation


 // output


 return EXIT_SUCCESS;
}

Мой вопрос: Есть ли более эффективный способ сделать это, кроме использования логических значений в цикле чтения / анализа?

1 Ответ

2 голосов
/ 17 октября 2011

Вы спрашиваете о чем-то «более эффективном», но, похоже, у вас нет конкретной цели производительности. То, что вы хотите здесь, вероятно, больше похоже на Code Review. Для этого есть сайт, в частности:

https://codereview.stackexchange.com/

Но все равно ...

Вы правы, интуитивно понимая, что четыре логических значения здесь на самом деле не нужны. Это 2 ^ 4 = 16 различных «состояний», во многие из которых вы никогда не попадете. (Ваша спецификация явно запрещает, например, keyword3_defined == true, когда after_keyword1 == false).

Состояние программы может храниться в перечислениях и логических значениях. Это позволяет «забытому» циклу пересмотреть строку кода при различных обстоятельствах, но при этом помнить, на какой стадии обработки он находится. Это полезно во многих случаях, в том числе в сложных синтаксических анализаторах. Но если ваша задача линейна и проста, лучше неявно «знать» состояние, основываясь на достижении определенной строки кода.

В качестве учебного примера, демонстрирующего контраст, о котором я говорю, приведу глупый конечный автомат для чтения букв A, за которыми следует любое количество букв B s:

enum State {
    beforeReadingAnA,
    haveReadAnA,
    readingSomeBs,
    doneReadingSomeBs
};

State s = beforeReadingAnA;
char c;
while(true) {
    switch (s) {
        case beforeReadingAnA: 
            cin >> c;
            if (cin.good() && c == 'A') {
                // good!  accept and state transition to start reading Bs...
                s = haveReadAnA;
            } else {
                // ERROR: expected an A
                return EXIT_CODE_FAILURE;
            };
            break;

         case haveReadAnA:
            // We've read an A, so state transition into reading Bs
            s = readingSomeBs;
            break;

         case readingSomeBs:
            cin >> c;
            if (cin.good() && c == 'B') {
                // good!  stay in the readingSomeBs state
            } else if (cin.eof()) {
                // reached the end of the input after 0 or more Bs
                s = doneReadingSomeBs;
            } else {
                // ERROR: expected a B or the EOF
                return EXIT_CODE_FAILURE;
            }
            break;

         case doneReadingSomeBs:
             // all done! 
             return EXIT_CODE_SUCCESS;
    }
}

Как уже упоминалось, это стиль кодирования, который может быть очень и очень полезным. Но для этого случая это смешно. Сравните с простым линейным фрагментом кода, который делает то же самое:

// beforeReadingAnA is IMPLICIT

char c;
cin >> c;
if (cin.fail() || c != 'A')
   return EXIT_CODE_FAILURE;

// haveReadAnA is IMPLICIT

do {
    // readingSomeBs is IMPLICIT

    cin >> c;
    if (cin.eof())
       return EXIT_CODE_SUCCESS;
    if (cin.fail() || c != 'B')
       return EXIT_CODE_FAILURE;
}

// doneReadingSomeBs is IMPLICIT

Все переменные состояния исчезают. Они не нужны, потому что программа просто «знает, где она». Если вы переосмыслите свой пример, то, вероятно, можете сделать то же самое Вам не понадобятся четыре логических значения, потому что вы можете навести курсор на строку кода и с уверенностью сказать, что эти четыре логических значения должны быть , если эта строка кода работает.

Что касается эффективности, то классы <iostream> могут сделать жизнь проще, чем у вас здесь, и быть более идиоматически C ++, не вызывая C-измы, такие как atof или когда-либо использовать c_str(). Давайте посмотрим на упрощенную выдержку из вашего кода, которая просто читает двойные числа, связанные с «keyword1».

string line;
getline(cin, line);
istringstream split(line);
vector<string> tokens;
char split_char = ' ';
string each;
while (getline(split, each, split_char)) {
    tokens.push_back(each);
}
double keyword1_first, keyword1_second;
if (tokens.size() > 2) {
    if (tokens[0] != "keyword1") {
        return EXIT_FAILURE; // input format error
    } else {
        keyword1_first = atof(tokens[1].c_str());
        keyword1_second = atof(tokens[2].c_str());
    }
}

Сравните это с этим:

string keyword;
cin >> keyword;
if (keyword != "keyword1") {
    return EXIT_FAILURE;
}
double keyword1_first, keyword1_second;
cin >> keyword1_first >> keyword1_second;

Magic. Iostreams может определить тип, который вы пытаетесь прочитать или написать. Если он сталкивается с проблемой интерпретации ввода так, как вы просите, то он оставит ввод в буфере, чтобы вы могли попробовать прочитать его другим способом. (В случае запроса строки, поведение состоит в том, чтобы прочитать серию символов до пробела ... если бы вы на самом деле хотели целую строку, вы бы использовали getline, как вы это сделали.)

Однако с обработкой ошибок вам придется иметь дело. Можно сказать iostreams использовать методологию обработки исключений, чтобы стандартным ответом на возникновение проблемы (например, случайным словом в месте, где ожидалось двойное число) было аварийное завершение вашей программы. Но по умолчанию устанавливается флаг сбоя, который необходимо проверить:

ошибочное поведение

В iostream есть нюанс, так что вы, вероятно, захотите провести некоторый обзор вопросов и ответов ... Я немного учился в последнее время, отвечая / спрашивая здесь:

Ошибка вывода, когда ввод не является числом. C ++

Когда использовать printf / scanf против cout / cin?

...