Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Вычисление выражений из файла, неверное считывается выражение 
V
    Опции темы
Linchx
Дата 31.7.2011, 17:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 31.7.2011

Репутация: нет
Всего: нет



Здравствуйте!

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

Код

#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


PM MAIL ICQ Jabber   Вверх
volatile
Дата 31.7.2011, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



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

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

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


Ну вы хоть бы для виду сначала поинтересовались что такое обратная польская запись.
а уж потом писать суровый код...  smile 
PM MAIL   Вверх
Teleport
Дата 31.7.2011, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 557
Регистрация: 5.7.2008
Где: Прибалтика

Репутация: нет
Всего: 6



Код

#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 еще не содержит тех элементов, которые  должны быть считаны из файла, а с ними уже пытаешься проводить операции.


--------------------
user posted image
user posted image 
PM MAIL   Вверх
Linchx
Дата 31.7.2011, 19:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 31.7.2011

Репутация: нет
Всего: нет



Цитата(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 + /


но у меня даже, если так вводить, то выдаст не верный ответ
PM MAIL ICQ Jabber   Вверх
volatile
Дата 31.7.2011, 19:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



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


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

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

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

PM MAIL   Вверх
Linchx
Дата 31.7.2011, 19:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 31.7.2011

Репутация: нет
Всего: нет



Цитата(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 / +


приоритет перепутал=)
PM MAIL ICQ Jabber   Вверх
volatile
Дата 31.7.2011, 20:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



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


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

поздравляю!

PM MAIL   Вверх
Teleport
Дата 31.7.2011, 20:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 557
Регистрация: 5.7.2008
Где: Прибалтика

Репутация: нет
Всего: 6



Цитата

Так понятно, что программа считывает изначально 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, 20:13


--------------------
user posted image
user posted image 
PM MAIL   Вверх
volatile
Дата 31.7.2011, 20:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



Teleport, там алгоритм обратной польской записи. подробнее здесь.

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

PM MAIL   Вверх
Linchx
Дата 31.7.2011, 20:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 31.7.2011

Репутация: нет
Всего: нет



Цитата(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 ] - неизвестно что.
Отсюда и надо думать.
Либо неправильный алгоритм чтения, либо неправильные данные на вход.

Скорее неправильные данные на вход, просто они уже должны быть в "постфиксной форме", а я их по-глупости в "инфиксной форме" ввожу. Сейчас проверил выражение в "постфиксной форме", считает.
PM MAIL ICQ Jabber   Вверх
Teleport
Дата 31.7.2011, 22:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 557
Регистрация: 5.7.2008
Где: Прибалтика

Репутация: нет
Всего: 6



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

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


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

Это сообщение отредактировал(а) Teleport - 31.7.2011, 22:47


--------------------
user posted image
user posted image 
PM MAIL   Вверх
Linchx
Дата 31.7.2011, 23:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 31.7.2011

Репутация: нет
Всего: нет



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

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


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

Teleport и volatile, спасибо большое! smile 
PM MAIL ICQ Jabber   Вверх
Linchx
Дата 1.8.2011, 04:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 31.7.2011

Репутация: нет
Всего: нет



Я ещё один вопрос возник. Как можно для входного выражения:
Код

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());


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

Это сообщение отредактировал(а) Linchx - 1.8.2011, 06:29
PM MAIL ICQ Jabber   Вверх
volatile
Дата 1.8.2011, 11:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



Не совсем понял откуда вы взяли последний код,
Если мои телепатические способности меня не подвели, 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(); а никак не в общем цикле.


PM MAIL   Вверх
Linchx
Дата 1.8.2011, 13:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 31.7.2011

Репутация: нет
Всего: нет



Цитата(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, 14:41
PM MAIL ICQ Jabber   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1190 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.