Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Вычисление выражений из файла


Автор: Linchx 31.7.2011, 17:46
Здравствуйте!

Суть задачи следующая: необходимо считать из файла выражение и на выходе получить результат его вычисления. Написал программу, за основу взял алгоритм "Обратной польской записи", на вход идет файл с выражением, программа его считывает производит операции арифметические, но не верно. Помогите разобраться с этой задачей.

Код

#include <fstream>
#include <cstring>
using namespace std;

int main(){
    char query[100];
    double stack[70],res;
    int m=0;
    ifstream fin("input.txt");
    ofstream fout("output.txt");
    if (fin == NULL) perror ("Error opening file");
    else{
        while(!fin.eof()){
            fin >> query;
            for (int i = 0; i < strlen(query); i++){
        if(query[i] >= '0' && query[i] <= '9'){
                    stack[m] = query[i] - '0';
                    m++;
                    continue;
            }
    switch (query[i]){
        case '+':{
        res = stack[m - 2]+stack[m - 1];
            break;
            }
        case '-':{
        res = stack[m - 2]-stack[m - 1];
            break;
            }
        case '*':{
        res = stack[m - 2]*stack[m - 1];
            break;
            }
        case '/':{
        res = stack[m - 2]/stack[m - 1];
            break;
            }
    }
        stack[m - 2] = res;
        m--;
    }
            fout << query << '=' << res << endl;
   }
  }
  fout.close();
  return 0;
}


вот, что подавал на вход:

5+5/2
6+9*7+10*5
5+5/2

итог на выходе:

5+5/2=1
6+9*7+10*5=0
5+5/2=2


Автор: volatile 31.7.2011, 19:07
Цитата(Linchx @  31.7.2011,  17:46 Найти цитируемый пост)
за основу взял алгоритм "Обратной польской записи",

Цитата(Linchx @  31.7.2011,  17:46 Найти цитируемый пост)
вот, что подавал на вход:

5+5/2
6+9*7+10*5
5+5/2


Ну вы хоть бы для виду сначала поинтересовались что такое обратная польская запись.
а уж потом писать суровый код...  smile 

Автор: Teleport 31.7.2011, 19:07
Код

#include <fstream>
#include <cstring>
#include <iostream>
using namespace std;
int main() {
    char query[ 100 ];
    double stack[ 70 ], res;
    int m = 0;
    ifstream fin( "input.txt" );
    ofstream fout( "output.txt" );
    if ( fin == NULL )
        perror( "Error opening file" );
    else {
        while ( fin >> query ) { //меняем условие
            for ( size_t i = 0; i < strlen( query ); i++ ) { //strlen возвращает size_t, а не int
                if ( query[ i ] >= '0' && query[ i ] <= '9' ) {
                    stack[ m ] = query[ i ] - '0';
                    cout << stack[ m ] << endl; // проверяем, что считали
                    m++;
                    continue;
                }

                cout << query[ i ] << endl;  //проверяем, что считали
                switch ( query[ i ] ) {
                case '+': { //вторая итерация цикла for в stack УЖЕ находится 5 и встречаем '+' а в stack только один элемент!
                    res = stack[ m - 2 ] + stack[ m - 1 ]; //неправильный исход операции сложения! так как 5 складывается неизвестно с чем! 
                    break;
                }
                case '-': {
                    res = stack[ m - 2 ] - stack[ m - 1 ];
                    break;
                }
                case '*': {
                    res = stack[ m - 2 ] * stack[ m - 1 ];
                    break;
                }
                case '/': {
                    res = stack[ m ] / stack[ m ];
                    break;
                }
                }
                stack[ m - 2 ] = res;
                m--;

            }

            cout << "End" << endl; //проверяем, что считали


            fout << query << '=' << res << endl;
        }
    }
    fout.close();
    return 0;
}

Гляди комментарии. Условие первого цикла неверно.
В общем, смысл тот, что stack еще не содержит тех элементов, которые  должны быть считаны из файла, а с ними уже пытаешься проводить операции.

Автор: Linchx 31.7.2011, 19:11
Цитата(volatile @ 31.7.2011,  19:07)
Цитата(Linchx @  31.7.2011,  17:46 Найти цитируемый пост)
за основу взял алгоритм "Обратной польской записи",

Цитата(Linchx @  31.7.2011,  17:46 Найти цитируемый пост)
вот, что подавал на вход:

5+5/2
6+9*7+10*5
5+5/2


Ну вы хоть бы для виду сначала поинтересовались что такое обратная польская запись.
а уж потом писать суровый код...  smile

Да, я понимаю, что вы хотите сказать. Нужно подавать вот в такой форме?

Код

5 5 2 + /


но у меня даже, если так вводить, то выдаст не верный ответ

Автор: volatile 31.7.2011, 19:30
Цитата(Linchx @  31.7.2011,  19:11 Найти цитируемый пост)
5 5 2 + /
но у меня даже, если так вводить, то выдаст не верный ответ 


Откуда вы знаете верный или не верный?

если вы думаете что
5 5 2 + /
это эквивалент
5+5/2
то вы ошибаетесь.

Еще раз, разберитесь сначала с польской записью, а только потом кодить.
Как вы можете писать программу, которая не знаете что должна делать?

Автор: Linchx 31.7.2011, 19:41
Цитата(Teleport @ 31.7.2011,  19:07)
Код

#include <fstream>
#include <cstring>
#include <iostream>
using namespace std;
int main() {
    char query[ 100 ];
    double stack[ 70 ], res;
    int m = 0;
    ifstream fin( "input.txt" );
    ofstream fout( "output.txt" );
    if ( fin == NULL )
        perror( "Error opening file" );
    else {
        while ( fin >> query ) { //меняем условие
            for ( size_t i = 0; i < strlen( query ); i++ ) { //strlen возвращает size_t, а не int
                if ( query[ i ] >= '0' && query[ i ] <= '9' ) {
                    stack[ m ] = query[ i ] - '0';
                    cout << stack[ m ] << endl; // проверяем, что считали
                    m++;
                    continue;
                }

                cout << query[ i ] << endl;  //проверяем, что считали
                switch ( query[ i ] ) {
                case '+': { //вторая итерация цикла for в stack УЖЕ находится 5 и встречаем '+' а в stack только один элемент!
                    res = stack[ m - 2 ] + stack[ m - 1 ]; //неправильный исход операции сложения! так как 5 складывается неизвестно с чем! 
                    break;
                }
                case '-': {
                    res = stack[ m - 2 ] - stack[ m - 1 ];
                    break;
                }
                case '*': {
                    res = stack[ m - 2 ] * stack[ m - 1 ];
                    break;
                }
                case '/': {
                    res = stack[ m ] / stack[ m ];
                    break;
                }
                }
                stack[ m - 2 ] = res;
                m--;

            }

            cout << "End" << endl; //проверяем, что считали


            fout << query << '=' << res << endl;
        }
    }
    fout.close();
    return 0;
}

Гляди комментарии. Условие первого цикла неверно.
В общем, смысл тот, что stack еще не содержит тех элементов, которые  должны быть считаны из файла, а с ними уже пытаешься проводить операции.

Спасибо, Teleport!

Так понятно, что программа считывает изначально 5 потом идущий за ней символ "+" и после 5. Хотя должен на вход получить 5 5 +, только как изначально из инфиксной в постфиксную перевести?

Спасибо ещё на счет size_t, теперь понятно на, что жаловался компилятор:
Код

comparison between signed and unsigned integer expressions


Добавлено через 6 минут и 33 секунды
Цитата(volatile @ 31.7.2011,  19:30)
Цитата(Linchx @  31.7.2011,  19:11 Найти цитируемый пост)
5 5 2 + /
но у меня даже, если так вводить, то выдаст не верный ответ 


если вы думаете что
5 5 2 + /
это эквивалент
5+5/2
то вы ошибаетесь.

Да, ошибся извиняюсь:
Код

5 5 2 / +


приоритет перепутал=)

Автор: volatile 31.7.2011, 20:04
Цитата(Linchx @  31.7.2011,  19:41 Найти цитируемый пост)
Да, ошибся извиняюсь:
5 5 2 / +


Ну вот видите, ваша программа оказывается делает именно то, что и должна делать.  smile 

поздравляю!

Автор: Teleport 31.7.2011, 20:12
Цитата

Так понятно, что программа считывает изначально 5 потом идущий за ней символ "+" и после 5. Хотя должен на вход получить 5 5 +, только как изначально из инфиксной в постфиксную перевести?


Ну вот смотри. 
В первой итерации у тебя считывается 5 и заносится как stack[ 0 ] m увеличивается на 1 и равно 1
Во второй итерации у тебя считывается '+' и в switch  у тебя оператор сложения 
Код

res = stack[ m - 2 ] + stack[ m - 1 ]

получается складывается stack[ 1 - 2 ] + stack [ 1 - 1 ]. В stack[ -1 ] - неизвестно что.
Отсюда и надо думать.
Либо неправильный алгоритм чтения, либо неправильные данные на вход.

Автор: volatile 31.7.2011, 20:21
Teleport, там алгоритм обратной польской записи. подробнее http://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D0%B0%D1%8F_%D0%BF%D0%BE%D0%BB%D1%8C%D1%81%D0%BA%D0%B0%D1%8F_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D1%8C

За исключением небольших неточностей (типа сравнения сигнед с ансигнед) программа делает именно то, что и должна делать.

Автор: Linchx 31.7.2011, 20:24
Цитата(Teleport @ 31.7.2011,  20:12)
Цитата

Так понятно, что программа считывает изначально 5 потом идущий за ней символ "+" и после 5. Хотя должен на вход получить 5 5 +, только как изначально из инфиксной в постфиксную перевести?


Ну вот смотри. 
В первой итерации у тебя считывается 5 и заносится как stack[ 0 ] m увеличивается на 1 и равно 1
Во второй итерации у тебя считывается '+' и в switch  у тебя оператор сложения 
Код

res = stack[ m - 2 ] + stack[ m - 1 ]

получается складывается stack[ 1 - 2 ] + stack [ 1 - 1 ]. В stack[ -1 ] - неизвестно что.
Отсюда и надо думать.
Либо неправильный алгоритм чтения, либо неправильные данные на вход.

Скорее неправильные данные на вход, просто они уже должны быть в "постфиксной форме", а я их по-глупости в "инфиксной форме" ввожу. Сейчас проверил выражение в "постфиксной форме", считает.

Автор: Teleport 31.7.2011, 22:37
2 volatile - это мне понятно и вы тоже об этом писали, но я хотел указать именно на то как работает код автора дабы можно было четко увидеть почему идет расчет именно так, а не как положено.  smile 
А сравнение типов я уже поправил еще в посте номер 3.
2 Linchx - самое важное - понять почему программа работала не так как ожидалось. Вроде, ты все понял  smile 
Цитата

Скорее неправильные данные на вход, просто они уже должны быть в "постфиксной форме", а я их по-глупости в "инфиксной форме" ввожу. Сейчас проверил выражение в "постфиксной форме", считает. 


Надо всегда разбираться с ошибками и знать точно - в чем именно каждая ошибка  smile 

Автор: Linchx 31.7.2011, 23:58
Цитата(Teleport @ 31.7.2011,  22:37)
2 volatile - это мне понятно и вы тоже об этом писали, но я хотел указать именно на то как работает код автора дабы можно было четко увидеть почему идет расчет именно так, а не как положено.  smile 
А сравнение типов я уже поправил еще в посте номер 3.
2 Linchx - самое важное - понять почему программа работала не так как ожидалось. Вроде, ты все понял  smile 
Цитата

Скорее неправильные данные на вход, просто они уже должны быть в "постфиксной форме", а я их по-глупости в "инфиксной форме" ввожу. Сейчас проверил выражение в "постфиксной форме", считает. 


Надо всегда разбираться с ошибками и знать точно - в чем именно каждая ошибка  smile

Teleport и volatile, спасибо большое! smile 

Автор: Linchx 1.8.2011, 04:40
Я ещё один вопрос возник. Как можно для входного выражения:
Код

5+5-2-
5-5/0

обработать ошибку и пропустить его. Таким образом отбросить не верно составленные выражения. Знаю, что можно при помощи оператора 'continue' пропустить, но условие не могу составить.

Код

fin >> p; //в массив передаю выражение
    if(*p>='a' && *p<='z'){ //проверяю на недопустимые символы
        fout << p << '=' << "can't calculate" << std::endl;continue; //пропускаю и возвращаюсь к массиву за новым выражением
    }
    prog = p; //prog внешняя переменная синтаксического анализатора 
    if(!*prog) break;
    eval_exp(&answer); //результат операций вычисления 
    fout << p << '=' << answer << std::endl;
  } while(!fin.eof());


а как запретить делить на ноль и ввод не верного арифметического выражения, не имею понятия. 

Автор: volatile 1.8.2011, 11:12
Не совсем понял откуда вы взяли последний код,
Если мои телепатические способности меня не подвели, smile 
то вероятно этот код должен вызывать процедуру eval_exp(), которая и вычисляет выражение?
Обработку ошибок нужно проводить именно в eval_exp(), и она должна возвращать код ошибки, или 0 если ошибок нет.

Обработать ошибку в eval_exp() - просто.
например деление на 0
               
Код

case '/': {
                    // здесь проверяем stack[ m - 1 ], и если 0, то return (ZERO_DIVIDE);
                    res = stack[ m - 2 ] / stack[ m - 1 ];
                    break;
кроме того обязательно добавить в свитч 
 
Код

    default:
            return (UNKNOWN_OPERAOR)

Там же обязательно проверку стека.
Ну и много чего еще предусмотреть...

Все это надо делать в в eval_exp(); а никак не в общем цикле.


Автор: Linchx 1.8.2011, 13:34
Цитата(volatile @ 1.8.2011,  11:12)
Не совсем понял откуда вы взяли последний код,
Если мои телепатические способности меня не подвели, smile 
то вероятно этот код должен вызывать процедуру eval_exp(), которая и вычисляет выражение?
Обработку ошибок нужно проводить именно в eval_exp(), и она должна возвращать код ошибки, или 0 если ошибок нет.

Обработать ошибку в eval_exp() - просто.
например деление на 0
               
Код

case '/': {
                    // здесь проверяем stack[ m - 1 ], и если 0, то return (ZERO_DIVIDE);
                    res = stack[ m - 2 ] / stack[ m - 1 ];
                    break;
кроме того обязательно добавить в свитч 
 
Код

    default:
            return (UNKNOWN_OPERAOR)

Там же обязательно проверку стека.
Ну и много чего еще предусмотреть...

Все это надо делать в в eval_exp(); а никак не в общем цикле.

Спасибо, но я код изменил, теперь считаю инфиксные выражения.

Пытаюсь присвоить переменной answer значение sn в случае ошибки она бы выводила сигнал, чтобы увидеть надпись wrong example, выводит но надпись "nan". Как можно изменить эту надпись? Может кто встречался с подобным!?

Код

//main.cpp
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "syntax.cpp"
#include <fstream>

char *prog;
void eval_exp(double *answer);

int main(void)
{
  double answer;
  char *p;
  p = (char *) malloc(100);
  if(!p) {
    printf("Memory allocation error.\n");
    exit(1);
  }

  std::ifstream fin("input.txt");
  std::ofstream fout("output.txt");
  if ( fin == NULL )
     perror( "Error opening file" );
  else {
  do {
    fin >> p;
    if(*p>='a' && *p<='z'){
        fout << p << '=' << "can't calculate" << std::endl;continue;
    }
    else{
    prog = p;
    if(!*prog) break;
    eval_exp(&answer);
    }
    if(answer==sn){
        fout << p << '=' << "can't calculate" << std::endl;continue;
    }
    else{
    fout << p << '=' << answer << std::endl;
    }
  } while(!fin.eof());
}
  return 0;
}


в syntax.cpp передается prog и answer, так же в syntax.cpp объявлено, что в случае недопустимых операций и арифметических выражений возвращать NaN для типа double. Но вот почему он вместо can't calculate записывает nan непонятно.

Код

//в syntax.cpp
#include <limits>

double sn = std::numeric_limits<double>::signaling_NaN();

void eval_exp(double *answer)
{
  get_token();
  if(!*token) {
      *answer=sn;
    return;
  }
  eval_exp2(answer);

  if(*token) *answer=sn;

Автор: Linchx 1.8.2011, 15:31
Цитата(Linchx @ 1.8.2011,  13:34)
Спасибо, но я код изменил, теперь считаю инфиксные выражения.

Пытаюсь присвоить переменной answer значение sn в случае ошибки она бы выводила сигнал, чтобы увидеть надпись wrong example, выводит но надпись "nan". Как можно изменить эту надпись? Может кто встречался с подобным!?

Код

//main.cpp
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "syntax.cpp"
#include <fstream>

char *prog;
void eval_exp(double *answer);

int main(void)
{
  double answer;
  char *p;
  p = (char *) malloc(100);
  if(!p) {
    printf("Memory allocation error.\n");
    exit(1);
  }

  std::ifstream fin("input.txt");
  std::ofstream fout("output.txt");
  if ( fin == NULL )
     perror( "Error opening file" );
  else {
  do {
    fin >> p;
    if(*p>='a' && *p<='z'){
        fout << p << '=' << "can't calculate" << std::endl;continue;
    }
    else{
    prog = p;
    if(!*prog) break;
    eval_exp(&answer);
    }
    if(answer==sn){
        fout << p << '=' << "can't calculate" << std::endl;continue;
    }
    else{
    fout << p << '=' << answer << std::endl;
    }
  } while(!fin.eof());
}
  return 0;
}


в syntax.cpp передается prog и answer, так же в syntax.cpp объявлено, что в случае недопустимых операций и арифметических выражений возвращать NaN для типа double. Но вот почему он вместо can't calculate записывает nan непонятно.

Код

//в syntax.cpp
#include <limits>

double sn = std::numeric_limits<double>::signaling_NaN();

void eval_exp(double *answer)
{
  get_token();
  if(!*token) {
      *answer=sn;
    return;
  }
  eval_exp2(answer);

  if(*token) *answer=sn;

Решил проблему, видно неправильно сравнивал условие:

было:
Код

if(answer==sn){
        fout << p << '=' << "can't calculate" << std::endl;continue;
    }


поменял:
Код

if(std::isnan(answer)){
        fout << p << '=' << "can't calculate" << std::endl;continue;
    }

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)