Калькулятор на С++ с двусвязным списком
Мне нужно сделать на С++ калькультор для консоли, выполняющий 4 действия (сложение, вычитание, умножение, деление).
Программа должна быть основана на принципах объектно-ориентированного программирования. Для промежуточного хранения чисел и знаков, надо использовать двусвязанный список.
1. Калькулятор должен работать как с целыми числами, так и с дробными числами и только в десятичной системе (Должен использоваться тип double).
2. Он должен работать "цепочкой" , т.е. производить действия не только над двумя числами, а например считать 3+8*9/15.
3. Должно учитываться приоритетное выполнение умножения и деления.
4. При делении на ноль должно выдаваться сообщение об ошибке
5. Программа должна быть бесконечным циклом, с возможностью прерывания пользователем (введение какого-либо специального знака).
Я что-то пробывала написать, но получается как-то кусками и мозги в кучку не собираются...... может быть кто-то сможет помочь...
#include <iostream>
#include <list>
using namespace std;
class Liste {
public:
double zahl;
char op;
Liste* next;
Liste* last;
Liste(double, char);
~Liste();
}
#include "Zulassungsaufgabe.h"
double zahl;
char op;
Liste* createList();
Liste* start=new Liste(zahl, op);
Liste* temp=start;
Liste* aftertemp=temp+1;
int main(){
while(op!='s', op!='=')
{
cout<<"Enter number: "<<zahl<<endl;
cin>>zahl;
cout<<"Enter operand: "<<op<<endl;
cin>>op;
Liste* neu= new Liste(zahl, op);
neu->last=temp;
temp=neu;
start->next=neu;
}
while (op=='*') {
temp->zahl=(temp->zahl)*(aftertemp->zahl);
temp->op=aftertemp->op;
temp->next=aftertemp->next;
temp->last=temp;}
while (op=='/') {
if(aftertemp->zahl==0){
cout<<"Fehler";}
else{
temp->zahl=(temp->zahl)/(aftertemp->zahl);
temp->op=aftertemp->op;
temp->next=aftertemp->next;
temp->last=temp;}}
while (op=='+') {
temp->zahl=(temp->zahl)+(aftertemp->zahl);
temp->op=aftertemp->op;
temp->next=aftertemp->next;
temp=temp->last;}
while (op=='-') {
temp->zahl=(temp->zahl)*(aftertemp->zahl);
temp->op=aftertemp->op;
temp->next=aftertemp->next;
temp->last=temp;}
}
http://gocha.org/webstart/parser/01/parser.pdf
Особо не вникал, но вроде бы много умных слов, да и код не совсем на C++, так что потребуется какая-то работа, а не чистое копирование. И списки там используются.
но, в общем, я бы сделал примерно так:
бесконечный цикл do{/*тут считываем выражение и разбираем его*/}while(/*проверка условия завершения цикла*/);
1) само выражение, которое нужно подсчитать я бы считывал в строку типа string(примеры использования)
2) после ввода, проверить чтобы в строке присутствовали только символы '0', ..., '9', '+', '-', '*', '/'. (если есть иные символы - выдать ошибку и перезапросить строку)
или входная строка есть завершающая последовательность(например "quit") - перейти к шагу 6.
3) разбить строку на компоненты вида [-]0..9, '+', '-', '*', '/'. компоненты поместить в двусвязный список.
получится последовательность вида: [число]-[мат. оператор]-[число]- ... - [число]
4) пройтись по списку и произвести операции... сделать, очевидно, нужно 2 прохода - в первом произвести умножение и деление, во втором - сложение и вычитание.
одна операция - это 3 элемента списка: 2 числа и оператор между ними
т.е. после вычисления операции в список вместо [число]-[оператор]-[число] нужно будет подставить результат вычисления операции, т.е. [число]
для преобразования строки в число можно использовать atof.
повторять пункт 4. пока в списке не останется 1 элемент, который и будет результатом вычислений
5) вывести результат и вернуться к шагу 1.
6) завершить программу
как то так наверно это всё нужно сделать...
п.с. если условием является использование ООП парадигмы, то пункты 2-4 можно оформить в один класс, который в качестве параметра конструктора будет принимать считанную строку и иметь метод, который будет вычислять результат...
какой-то такой велосипедик у меня в голове получился полвторого ночи)
3) разбить строку на компоненты вида [-]0..9, '+', '-', '*', '/'. компоненты поместить в двусвязный список.
получится последовательность вида: [число]-[мат. оператор]-[число]- ... - [число]
4) пройтись по списку и произвести операции... сделать, очевидно, нужно 2 прохода - в первом произвести умножение и деление, во втором - сложение и вычитание.
одна операция - это 3 элемента списка: 2 числа и оператор между ними
т.е. после вычисления операции в список вместо [число]-[оператор]-[число] нужно будет подставить результат вычисления операции, т.е. [число]
п.с. если условием является использование ООП парадигмы, то пункты 2-4 можно оформить в один класс, который в качестве параметра конструктора будет принимать считанную строку и иметь метод, который будет вычислять результат...
Спасибо за совет)))) Как это все примерно должно работать я себе как раз представляю, а вот как это написать, уже проблематично, т.к. я вообще С++ увидела месяц назад и что-то все очень трудно идет....
Я не понимаю, как сделать пункты 3 и 4...это хоть примерно похоже на то, что я написала??
И что такое ООП парадигмы?? Мне в идеале надо сделать класс списка, не в идеале нужен хоть один какой угодно класс....
Обратную польскую запись я уже смотрела, но опять же там все сохраняется в стэк и я не представляю как сохранить это все в двусвязанный список
Можно использовать приоритетную очередь на основе двусвязного списка:
кстати да, тогда можно сделать будет всего 1 проход... но код это усложнит)
про + можно невзначай забыть, а - всегда относить к числу, заменяя его в очереди операций сложением.
а да... нужно обрабатывать ситуацию, когда предыдущий символ был операцией...
[QUOTE="Norgat"]про + можно невзначай забыть, а - всегда относить к числу, заменяя его в очереди операций сложением.[/QUOTE]
Да, я просто к тому, что нужно это учитывать хотя бы на уровне парсера. Не помню почему, но я добавлял 0 перед унарной операцией.
З.Ы. Пороюсь сегодня в старых дисках, может и найду этот говнокод :) Тогда выложу, если интересно будет.
что то оно как то не логично делать две очереди)
вроде ж всё можно в одну упихать и хранить в строках, а при проведении операции гонять туда-сюда преобразованиями строка->число и обратно (оно конечно не ахти как круто, но мы тут и не калькуляторы National Instruments делаем:D)
З.Ы. Пороюсь сегодня в старых дисках, может и найду этот говнокод :) Тогда выложу, если интересно будет.
ну я вот что то набросал на больную голову... вообщем парсинг строки выражения:
#include <conio.h>
#include <deque>
#include <string>
using namespace std;
class calculator
{
private:
deque<string> term;
public:
calculator(string& str)
{
string buffer = "";
if((str[0]!= '*') && (str[0]!= '/') && (str[0]!= '+'))
{
buffer += str[0];
for(int i = 1;i < str.length(); ++i)
{
if((str!= '*') && (str!= '/') && (str!= '+') && (str!= '-'))
buffer += str;
else
if(str == '-')
{
if((term.back()[0] == '*') || (term.back()[0] == '+') || (term.back()[0] == '/'))
buffer += '-';
else
{
term.push_back(buffer);
buffer.clear();
term.push_back("+");
buffer += '-';
}
}
else
{
term.push_back(buffer);
buffer = str;
term.push_back(buffer);
buffer.clear();
}
}
term.push_back(buffer);
}
else
cerr << "Error parsing\n";
}
void print_deq()
{
deque<string>::iterator I = term.begin();
while(I!= term.end())
{
cout << I->data() << endl;
++I;
}
}
};
void main()
{
string str;
cout << "Enter term: ";
cin >> str;
calculator C(str);
C.print_deq();
_getch();
}
правда он далеко не идеален:
- пробелы вставлять крайне не рекомендуется;
- нет проверки входной строки на недопустимые символы;
п.с. Итератор