Odwrócona notacja polska

0

Witam,
mam za zadanie napisanie kalkulatora odwróconej notacji polskiej z wykorzystaniem własnej implementacji stosu. Mam stos samorozszerzający się, z którego usunąłem zbędne metody. Nie jestem wirtuozem programowania, ale zawsze staram się pisać kod po swojemu bez kopiowania. Coś jest na pewno źle (bo nie działa), ale ciężko jest mi to zlokalizować. Po kilku godzinach prób, rozpisywania na kartce i przeszukiwania internetu dochodzę do wniosku, że może lepiej jest to tu wrzucić. Może jakaś mądra głowa spojrzy i od razu stwierdzi co jest źle, a może po prostu trzeba to napisać od nowa.
Z góry dziękuję za pomoc.

#include <iostream>
#include <sstream>
#include <string>
using namespace std;


struct lista
{
    lista *next;
    double dane;
};

class stack
{
 lista *stos1;
 public:
     stack();
     ~stack();
     void push(double);
     void pop();
     lista *value(); 
};

stack::stack()
{
    stos1=NULL;             //zerowanie wskaznika na pierszy element stosu (head)
}

stack::~stack()
{
    while (stos1)           //dopoki jest cos na glowie to usuwaj
    {
        pop();
    }
}

void stack::push(double v)
{
    lista *e = new lista;   //stworzenie nowej zmiennej typy strukturowego
    e->dane = v;            //przypisanie do zmiennej podanej wartosci
    e->next = stos1;        //przypisanie do wzkaznika szczytu
    stos1 = e;              //nowy szczyt
}

void stack::pop()
{
    if (stos1)
    {
        lista *e = stos1;   //zapamietanie bierzacego szczuty
        stos1 = stos1->next;//zamiana wskaznika
        delete e;           //usuniecie e
    }
}
lista *stack::value()
{
    return stos1;
}
int main()
{
    stack stos1;
    stringstream strumien;
    string ciagZnakow="33+";
    double cyfra_znak;
    double a,b,c;
    int dlugoscCiaguZnakow=ciagZnakow.length()-1;
    for(int i=0; i<=dlugoscCiaguZnakow; i++) //petla odczytujaca kazdy znak ze stringa
    {
        strumien<<ciagZnakow[i];
        if(strumien>>cyfra_znak)        //jezeli to co jest w strumieniu jest typu liczbowego (double) to  na stos
        {
            stos1.push(ciagZnakow[i]);
        }
        else                            // jak nie to:
        {
            a=stos1.value()->dane;      //wartosc ze szczytu do zmiennej a
            stos1.pop();                //zdjecie wartosci ze szczytu
            b=stos1.value()->dane;      //wartosc ze szczytu do zmiennej b
            stos1.pop();                //zdjecie wartosci ze szczytu
            switch(ciagZnakow[i])          //w zaleznosci od tego co jest w zmiennej 'cyfra_znak'
            {
                case '+': c=a+b; break;
                case '-': c=a-b; break;
                case '*': c=a+b; break;
                case '/': c=a+b; break;
            }
            stos1.push(c);              //wynik na stos
        }
    }
    cout<<stos1.value();
    return 0;
}
2
Shimek napisał(a):

Po kilku godzinach prób, rozpisywania na kartce i przeszukiwania internetu dochodzę do wniosku, że może lepiej jest to tu wrzucić. Może jakaś mądra głowa spojrzy i od razu stwierdzi co jest źle, a może po prostu trzeba to napisać od nowa.

Dobra ściema.

#include <iostream>
#include <sstream>
#include <string>
using namespace std;


struct lista
{
    lista *next;
    double dane;
};

class stack
{
 lista *stos1;
 public:
     stack();
     ~stack();
     void push(double);
     void pop();
     lista *value(); 
};

Użyj std::vector. Jak musisz koniecznie zaimplementować listę i stos, to zastanów się nad nie używaniem nagich wskaźników.

        strumien<<ciagZnakow[i];
        if(strumien>>cyfra_znak)        //jezeli to co jest w strumieniu jest typu liczbowego (double) to  na stos

3 poszła do strumien.
3 została wyciągnięta ze strumien. strumien ustawił eofbit (dokumentacja!), kolejny zapis do strumien nie wyzewruje flag, nie da się już wykonać żadnego poprawnego odczytu bez zawołania clear ...

I na koniec, zaprzyjaźnij się z debugerem.

2

Na pewno błąd jest tu:

                case '*': c=a+b; break;
                case '/': c=a+b; break;

Ale może istnieć wiele innych.

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
using namespace std;

double opp(string str)
{
	vector<double> stack;
	istringstream is(str);
	while(is)
	{
		double value;
		if(is>>value) stack.push_back(value);
		else
		{
			is.clear(ios::goodbit);
			string ch;
			is>>ws>>ch;
			double b=stack.back();
			stack.pop_back();
			double a=stack.back();
			stack.pop_back();
			if(ch=="add") stack.push_back(a+b);
			else if(ch=="sub") stack.push_back(a-b);
			else if(ch=="mul") stack.push_back(a*b);
			else if(ch=="div") stack.push_back(a/b);
			else if(ch!="") cout<<"Bad operation: "<<ch<<endl;
			else break;
		}
	}
	return stack.front();
}

int main()
{
	cout<<opp("3 3 add 7 mul");
	return 0;
}
3

Powyżej Masz rozwiązanie, ale akurat, dydaktyka, nie jest mocną stroną @_13th_Dragon :). Jak Piszesz stos "samorozszerzający się", czyli po polsku stos dynamiczny, to lepiej na bazie std::vector, a nie linked listy.
Metody stosu:

  • push;
  • pop;
  • isEmpty.

Te muszą być, co tam robi robi lista *value();, to jest złamanie istoty ADT .
Co do samego algorytmu, nie case, Zrób funkcje: add, divide, etc... Wejściowego stringa lepiej "stokenizować", czyli: 33+ -> ["3", "3", "+"], (wektor stringów) będzie łatwiej, Zrobisz to:
if(strumien>>cyfra_znak),
po ludzku, bo jest jakieś dziwadło.

https://www.fluentcpp.com/2017/04/21/how-to-split-a-string-in-c/

0

Totalna parodia.

  1. Notacja postfiksowa została wymyślona, aby wyliczać normalne wyrażenia, czyli infiksowe, np.:
    1 + 23^2 + 2sin(3*pi/4)
    przekształcamy na:
    1 2 3 2 ^ * + 2 3 pi 2 / sin *

  2. i to jest już gotowy wynik, który wyliczamy łatwo - z użyciem stosu!

o tak:
push(1)
push(2)
push(3)
push(2)

teraz mamy operację: ^
zatem zdejmujemy argumenty i wykonujemy:
push(pop ^ pop)
itd.

Tworzenie 'stosu' z gotowego wyrażenia w postfiksie jest bezcelowe - to się po prostu oblicza za pomocą stosu!

2
bonifacy napisał(a):

Tworzenie 'stosu' z gotowego wyrażenia w postfiksie jest bezcelowe - to się po prostu oblicza za pomocą stosu!

Nie zupełnie, jest to całkiem dobre zadanie dydaktyczne na tworzenie własnego stosu i posługiwanie się nim.

0

Pewnie że można zrobić sobie stos z wyrażeniami w tej odwrotnej notacji,
ale to ma sens jedynie w przypadku, gdy te obliczenia będą wykonywane wielokrotnie, tz. w przypadku gdy w wyrażeniu występują zmienne.

Przykład - chcemy narysować wykres funkcji, którą wprowadza użytkownik, np.:
z = 2cos(sqrt(1 - x^2-y^2)) + 5y;

wtedy przerabiamy to na wygodne - proste do obliczania wyrażenie, czyli notacja postfiksowa;
no i wyliczamy to np. 10000 razy, bo po siatce: x = 0..1 i y = ... z krokiem 0.01;

Tyle że teraz na stosie mamy symbole - obiekty reprezentujące liczby i operatory, a nie proste liczby.

push symbol number 32.6
push symbol = variable x
push symbol function sin, 1 arg
push function add, 2 args
push vector, 4 dim
...

1
bonifacy napisał(a):

Pewnie że można zrobić sobie stos z wyrażeniami w tej odwrotnej notacji,
ale to ma sens jedynie w przypadku, gdy te obliczenia będą wykonywane wielokrotnie, tz. w przypadku gdy w wyrażeniu występują zmienne.

A możesz podać chociażby jeden sensowny a zarazem nie czysto dydaktyczny przypadek, w którym zmienne nie są potrzebne?

0

standardowe kalkulatory nie pozwalają na używanie zmiennych.

(3 + 4.5)*5 - 3 = ... ok.

natomiast:
sin(x) = ?
i już żaden kalkulator tego nie ogarnie, i nawet nie pozwoli tego wpisać - bo on nie zna pojęcia 'zmiennej'.

obiekt typu liczba = zmienna lub stała.

Przykład, jak wygląda wyliczanie takiego wyrażenia - na symbolach:

double calcPost(const Symbol *s)
{
   double stack[32], *st = stack;

   for(; s->action != C_NIC; s++)
     {
       if( s->action == C_NUM || s->action == C_LVAL ) *st++ = *s; // push: zmienna lub stała
       else // operatory
        {
      	  int a = ( s->isRL() ) ? 2 : s->nArgs();  // potrzebujemy liczbę argumentów

          if( a == 1 ) st[-1] = s->f1(st[-1]);
          else //if( n == 2 ) 
            {
              st--; // pop
              st[-1] = s->f2(st[-1], st[0]); // obliczenie + pop i push wynik operacji...
            }
        }
     }

   return stack[0]; // finalny wynik zawsze jest na wierzchołku
}
2
bonifacy napisał(a):

Totalna parodia.
(...)
Tworzenie 'stosu' z gotowego wyrażenia w postfiksie jest bezcelowe - to się po prostu oblicza za pomocą stosu!

Pytanie dotyczy konkretnego problemu, który zaprezentował op, a nie zastosowania ONP. Nie wiem, co w tym niezrozumiałego.
Wyodrębnienie podproblemu z szerszego zagadnienia ma swoje plusy w dydaktyce. W tym wypadku zrozumienie języka programowania, typowych konstrukcji programistycznych czy przykładowo istoty tokenizacji w parsowania. Op jest ewidentnie na początku drogi z programowaniem i ma w aktualnym momencie problemy natury implementacyjnej. Mam więc wątpliwości czy ironiczny ton i "o tak, push, push, pop i masz kalkulator gotowy" jest pomocne.

1

@bonifacy:

Notacja postfiksowa została wymyślona, aby wyliczać normalne wyrażenia, czyli infiksowe, np.

Tu Piszesz o innym problemie: infix to postfix parsing.
O jakich kalkulatorach mówisz, chybaz lat 60 - tych :-)

0

Nie wiem kto i co tu stworzyć chce - pewnie bicz z gówna?

  1. Stos to jest zwyczajna tablica, której koniec (wierzchołek) pamiętamy
  2. można taki stos powiększać 'w locie', ale w praktyce jest to zbyteczne - w 99.99%.
  3. operacje na stosie robimy... tak samo jak na tablicy - nie ma tu o czym gadać;
    push i pop to banały...
  4. nie ma stosów listowych, co tu próbuje produkować autor - to jest bezużyteczna bzdura!
  5. listy: jedno i dwu-kierunkowe - to banały z lat 70-tych, nikt tego nie używa... a może nawet nigdy nie było to używane!
0

Przykład praktycznej Implementacji stosu 'dynamicznego':

push(value)
{
if( free_space < value.size ) resizeFibonacci(); // !
// dalej standard
copy(top, value);
top += value.size;
}

stack::resizeFibbonacci()
{
newsize = nextFibonaccinumber(size); // np. 2 -> 3, 3 -> 5, 5 -> 8, 8 -> 13, itd.
realloc(stack, newsize);
}

zasada jest prosta: wszelki wzrost w naturze ma tendencję do wzrostu w tempie ciągu Fibonacciego,
więc należy to uwzględnić - w celu optymalizacji kodu!

Można próbować wzrost poprzez podwajanie: n -> 2n, ale to już przesada - za szybko, i gorzej wyjdzie w praktyce! :)

1

Myślałem, ze Trolujesz, ale widze, że coś tam Wiesz:). Jaki to przyklad implementacji, mix pseudokodu i C, niekompletna metoda push i resize, a co dalej, Potrafisz pociągnąc?:)

0

Nie żartuj sobie ze starego zawodowca, bo jeszcze przypadkiem wygrasz a wtedy przegrasz. :)

0

Przegra, wygra, zremisuje, a jak się spotkamy na rekrutacji, to mnie Przyjmiesz?:)

0

Nie znam się na 'rekrutacji', i nie chcę.. - to wasz światek, bawcie się.

1

Odbieglismy od tematu, fajny fibonacci heap Zapodałeś, Mógłbyś rozwinąć, jakiś pseudokod? Bym sobie popaczał.

0

Ten motyw z fibonaccim ma istotne znaczenie dopiero w dużych bazach danych, w przypadku stosu to jest w zasadzie banał;
bo tu można sobie z mety zaimplementować stos: 100 x średnia (przewidywalna), i będzie dobrze - bez realokacji.

Generalna uwaga: matematyka, czyli inaczej: numerologia, została zakazana z 500 lat temu - przez Wielką Inkwizycję, zatem nie wolno tego używać:

dlaczego?

sin(666 stopni) = -Phi/2;

cos(6 x 6 x 6 = 216 stopni) = ?

666 = 6 x 111;
111 = 3 x 37.
37 = ?

wszelka użyteczna wiedza jest zakazana, jednie pierdoły wolno nauczać.

0

No to masz gotowy kalkulator, który robiłem z 20 lat temu:
https://easyupload.io/gqgbfu
wersja testowa.

0

Ok, to może po kolei.
Po pierwsze dziękuję bardzo za zaangażowanie się w odpowiedź. Nie spodziewałem się takiej dyskusji.
Po drugie dzięki za podsunięcie mi std::vector. To jest coś pięknego. Stos który mi wcześniej zajmował ponad 100 lini teraz zajmuje 32, choć pewnie dałoby się go jeszcze skrócić. Poza tym napisałem go w 10min gdzie wcześniej ... yyy nie napiszę ile mi to zajęło ;), ale jak teraz to napisałem w 10 min to byłem w lekkim szoku, że się tak da.
Dalej, jak nalik zauważył jestem totalnym szczypiorem w programowaniu, a musze napisać trochę tego typu programów gdyż wykładowca tego wymaga
Kolejna kwestia to widzieć wasze zaangażowanie i ewidentną pasję to jest moc. Przede wszystkim uwielbiam ludzi z pasją, ale teraz to i ja mam o wiele większą motywację do nauki. Właściwie to teraz jak wracam z pracy to od razu siadam do kodu (tak, studiuję zaocznie). Wcześniej musiałem się zmuszać. Po moim pierwszym poście dzięki Waszym odpowiedziom już lubię te forum i na pewno będę często tu bywał.

To teraz a propos kalkulatora. Przepisałem ten udostępniony przez _13th_Dragon z małymi zmianami. Jak wiadomo gdy się coś przepisuje to też się uczy. No i wydaje mi się, że rozumiem wszystko co jest tam zawarte, jednak zastanawiam się dlaczego tutaj:

            if(znak=="add") stos.push_back(a+b);
            else if(znak=="sub") stos.push_back(a-b);
            else if(znak=="mul") stos.push_back(a*b);
            else if(znak=="div") stos.push_back(a/b);
            else if(znak!="") cout<<"Bad operation: "<<znak<<endl;
            else break;

nie mogę użyć znaków dodawania, odejmowania itd.

            if(znak=="+") stos.push_back(a+b);
            else if(znak=="-") stos.push_back(a-b);
            else if(znak=="*") stos.push_back(a*b);
            else if(znak=="/") stos.push_back(a/b);
            else if(znak!="") cout<<"Bad operation: "<<znak<<endl;
            else break;

W treści mojego zadania jest przykład danych wejściowych i to powinny być owe znaki. Próbowałem i chciałem się nawet pochwalić gotowym kodem, ale nie wyszło. Dla stringa = "3 3 +"; konsola pokazuje tylko 3.

PS Odpisuję dopiero teraz bo w między czasie robiłem inne zadania na reprezentacje grafów w komputerze. Nie wykluczam, że może też będę później potrzebował w tym temacie pomocy, ale to już w innym poście

1

Nie działa ponieważ użyłem if(is>>value) a wg mnie jest skopane dla wartości liczbowych, zwyczajnie jeżeli napiszemy w strumieniu będzie +x to ten pierwszy plus zostanie "zjedzony".
W sumie da się to obejść:

double opp(string str)
{
    vector<double> stack;
    istringstream is(str);
    for(string word;is>>ws>>value;)
    {
            char *end;
            double value=strtod(word.c_str(),&end);
            if((*word.c_str())&&(!*end)) stack.push_back(value);
            else
            {
                double b=stack.back();
                stack.pop_back();
                double a=stack.back();
                stack.pop_back();
                if(ch=="+") stack.push_back(a+b);
                else if(ch=="-") stack.push_back(a-b);
                else if(ch=="*") stack.push_back(a*b);
                else if(ch=="/") stack.push_back(a/b);
                else if(ch!="") cout<<"Bad operation: "<<ch<<endl;
                else break;
            }
    }
    return stack.front();
}

ale nadal będą problemy z:
2 3+ trzeba koniecznie podać 2 3 +
1 2 3 +* trzeba koniecznie podać 1 2 3 + *
1 2 +7 * trzeba koniecznie podać 1 2 + 7 *
Aby było dobrze to trzeba bardziej sensownie analizować podawany ciąg.

0

A ja mam pytanie natury semantycznej: co to jest w tej waszej psedoprogramistycce: push_back?

może zróbta, np.: push_forth, push_last, push_anything, a wtedy może załapię waszą logiczkę. :)

3

push_back to metoda na std::vector z STL, więc wszyscy wiedzą, co oznacza.

0

Nie wiem kto tu jest trolem, czy blagierem,
ale wiem że implementacja stosu za pomocą stl to parodia, niestety.

1

Przecież stos jest w STL, to jak parodia.

2
bonifacy napisał(a):

A ja mam pytanie natury semantycznej: co to jest w tej waszej psedoprogramistycce: push_back?

może zróbta, np.: push_forth, push_last, push_anything, a wtedy może załapię waszą logiczkę. :)

Pokaż nam jak wygląda prawdziwa programistyka i oświeć nas, zagubionych!

0
lion137 napisał(a):

Przecież stos jest w STL, to jak parodia.

Trza było od razu powiedzieć to autorowi tego tematu...
o czym wy tu ogóle gadacie - marnujecie czas innych, bo nie macie nic innego do roboty?

tajny_agent napisał(a):
bonifacy napisał(a):

A ja mam pytanie natury semantycznej: co to jest w tej waszej psedoprogramistycce: push_back?

może zróbta, np.: push_forth, push_last, push_anything, a wtedy może załapię waszą logiczkę. :)

Pokaż nam jak wygląda prawdziwa programistyka i oświeć nas, zagubionych!

Normalnie wygląda... niby nie widziałeś jeszcze programów użytkowych, czy jak?!
No to mogę ci pokazać kilka...

1

@bonifacy: nie róbmy z ludzi ułomów, przecież sobie umie wpisać w wyszukiwarkę: "push_back c++"

1 użytkowników online, w tym zalogowanych: 0, gości: 1