Koło informatyczne 2012

Kalkulator ONP

Odwrotna Notacja Polska ONP (ang. RPN - Reverse Polish Notation) jest sposobem zapisu wyrażeń arytmetycznych, w którym zbędne stają się nawiasy. W ONP najpierw podajemy argumenty, a dopiero po nich określamy operację do wykonania:

 

a b + oznacza a + b
a b * oznacza a * b

 

Na dzisiejszych zajęciach zaprogramujemy prosty kalkulator działający w ONP. Dane wprowadzane do kalkulatora będą przechowywane na stosie pięcio-elementowym. Działania będą zawsze wykonywane na elementach znajdujących się na szczycie stosu.

 

Projekt Kalkulatora ONP

Utwórz na swoim dysku twardym odpowiedni katalog dla projektu. My kolejne projekty zapisujemy w numerowanych katalogach: 001, 002, itd. Dzięki temu pliki nigdy się nie mieszają.

Uruchom środowisko Borland C++ Builder 6 Personal. Projekt zapisz w utworzonym wcześniej katalogu za pomocą opcji menu:

 

File → Save Project As...

 

Pierwszy zapisywany jest plik modułu. Pozostaw jego nazwę niezmienioną: Unit1. Drugi plik jest plikiem projektu. Tutaj zmień nazwę na rpncalc. Jeśli wykonałeś poprawnie tę operację, to w katalogu projektu powinny znaleźć się następujące pliki:

 

rpncalc.bpr - plik zawiera informacje dotyczące projektu
rpncacl.cpp - plik programu projektu
rpncalc.res - tzw. plik zasobów projektu
Unit1.cpp - plik modułu obsługującego okno główne
Unit1.dfm - plik definiujący parametry użytych komponentów w oknie głównym
Unit1.h - plik nagłówkowy modułu

 

Tworzenie programu w Borland C++ Builder składa się zwykle z dwóch etapów. Na początku projektujemy wygląd naszej aplikacji, ustawiamy komponenty, określamy ich położenie, wielkość i własności, a następnie dodajemy do nich obsługę zdarzeń, czyli określamy, co program ma robić, gdy użytkownik wykona jakąś akcję (np. kliknie myszką w przycisk). Teraz po kolei stworzymy interfejs naszego kalkulatora.

Interfejs aplikacji

W oknie Object Inspector znajdują się własności okna programu. Będziemy je zmieniać, idąc od góry na dół.

BorderIcons→biMaximize = false
Parametr ten powoduje wyłączenie przycisku maksymalizacji. Użytkownik będzie mógł jedynie zminimalizować lub zamknąć okno kalkulatora.

BorderStyle = bsSingle
Rozmiarów okna użytkownik nie będzie mógł zmieniać.

Caption = Kalkulator ONP
Tytuł okna programu

Name = frmCalc
Pod taką nazwą będzie widoczny w programie egzemplarz klasy naszego okna. Umawiamy się, że pierwsze trzy literki nazwy zawsze określają rodzaj obiektu - dzięki temu szybko znajdziemy go na liście w oknie Object TreeView - frm to Form, czyli okno.

Position = poDesktopCenter
Po uruchomieniu programu nasze okno pojawi się na środku pulpitu.

Na tak przygotowanym oknie umieścimy teraz komponent etykiety - Label. Etykiety pozwalają wyświetlać dowolny tekst. Kliknij dwukrotnie w ikonę A na pasku narzędziowym komponentów. Następnie w Object Inspector ustawiamy własności etykiety:

Alignment = taRightJustify
Tekst będzie dosuwany do prawej krawędzi obszaru zajmowanego przez etykietę.

Autosize = false
Etykieta nie będzie się automatycznie dostosowywała do szerokości zawartego w niej tekstu.

Left = 8
Pozycja lewej krawędzi etykiety na obszarze okna.

Top = 8
Pozycja górnej krawędzi etykiety na obszarze okna.

Width = 249
Szerokość etykiety

Kliknij raz myszką w etykietę, aby ją wybrać, a następnie skopiuj do schowka za pomocą klawiszy Ctrl+C. Teraz wklej cztery razy etykietę ze schowka do okienka. W efekcie na oknie będzie się znajdowało 5 etykiet. Ustaw je jedna pod drugą za pomocą myszki:

obrazek

Wybierz myszką ostatnią etykietę u dołu i zmień w oknie Object Inspector jej własność Name na lblS0 (lbl oznacza Label, czyli etykietę). Idąc w górę nazwij kolejne etykiety lblS1...lblS4. Przy najwyższej etykiecie napis zmieni się na lblS4 - nie przejmuj się tym, tak ma być.

obrazek

Etykiety te będą nam służyły jako prosty stos. Będziemy w nich przechowywać liczby w postaci napisów.

Pod etykietami umieść komponent Edit, powiększając go na odpowiednią szerokość. Umożliwia on wprowadzanie tekstu. Jego własność Name ustaw na edtInput (edt oznacza Edit).

obrazek

Teraz umieść w oknie komponent przycisku - Button. Pomniejsz go myszką na szerokość Width = 35 (gdy skalujesz komponent, to w żółtym prostokącie pokazywane są jego aktualne wymiary). Tak przygotowany przycisk skopiuj do schowka (Ctrl+C), a następnie wklej 9 razy - w oknie powinno pojawić się 10 przycisków. Umieść je w dwóch rzędach pod komponentem edtInput. W pierwszym rzędzie 6 przycisków (ostatni przycisk możesz trochę poszerzyć, aby zgrał się z komponentem edtInput), w drugim 4:

obrazek

Pozmieniaj własności Caption przycisków (na przycisku minus umieszczamy 3 minusy, ponieważ jeden jest zbyt mało widoczny):

obrazek

Teraz odpowiednio zmienimy własności Name każdego z przycisków:

obrazek

(btn oznacza Button, czyli przycisk).

Funkcje przycisków będą następujące:

btnCLR - zeruje cały kalkulator, umieszcza 0 we wszystkich elementach stosu, czyści tekst w edtInput.
btnADD - dodaje dwa ostatnie elementy stosu
btnSUB - odejmuje dwa ostatnie elementy stosu
btnMUL - mnoży dwa ostatnie elementy stosu
btnDIV - dzieli dwa ostatnie elementy stosu
btnENT - przepisuje liczbę z edtInput na stos
btnDEL - usuwa ze stosu ostatni element
btnDUP - kopiuje na stos ostatni element
btnEX - zamienia miejscami dwa ostatnie elementy na stosie
btnSQR - oblicza pierwiastek kwadratowy z ostatniego elementu stosu

Dopasuj rozmiary okna programu:

obrazek

Kliknij myszką przycisk btnENT i ustaw jego własność Default na true. Spowoduje to, iż przycisk ten będzie automatycznie aktywowany za każdym razem, gdy naciśniesz klawisz Enter. Zwróć uwagę, że przycisk ten jest rysowany w oknie w czarnym prostokącie:

obrazek

Skompiluj i uruchom aplikację. Interfejs jest gotowy, teraz zaprogramujemy jego obsługę.

Obsługa zdarzeń

Uwaga. Przy tworzeniu funkcji obsługujących zdarzenia należy korzystać z opcji środowiska Borland C++ Builder, a nie wpisywać kod ręcznie.

Na początek zaprogramujemy zdarzenie kliknięcia przycisku btnCLR. Funkcję obsługi tego zdarzenia tworzysz szybko klikając dwukrotnie myszką w przycisk C. Środowisko przenosi cię do edytora kodu programu, gdzie powinieneś zobaczyć pustą funkcję:

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnCLRClick(TObject *Sender)
{

}
//---------------------------------------------------------------------------

 

Funkcja ta została utworzona w pliku modułu Unit1.cpp. Jest to funkcja składowa klasy TfrmCalc, czyli naszego okna. Wpisz do niej następujący kod:

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnCLRClick(TObject *Sender)
{
    lblS0->Caption = "0"; // zerujemy kolejne elementy stosu
    lblS1->Caption = "0";
    lblS2->Caption = "0";
    lblS3->Caption = "0";
    lblS4->Caption = "0";
    edtInput->Text = ""; // czyścimy wiersz wejścia
}
//---------------------------------------------------------------------------

Dostęp do tekstu wyświetlanego przez etykiety uzyskujemy poprzez ich własności Caption. Wiersz edycji jest dostępny poprzez własność Text. Jeśli teraz skompilujesz i uruchomisz aplikację, to po kliknięciu w przycisk C nastąpi wyzerowanie kalkulatora:

obrazek

Jednakże efekt ten chcielibyśmy uzyskać od razu na początku programu, aby kalkulator startował od tego stanu. Zamknij aplikację i przejdź do okna interfejsu (jeśli nie jest widoczne, wciśnij klawisz F12, który przełącza pomiędzy edytorem kodu a oknem interfejsu). Kliknij myszką w obszar niezajęty przez komponenty, aby w Object Inspector dostać własności okna (możesz również wybrać obiekt frmCalc w oknie Object TreeView). Teraz przejdź na zakładkę Events (zdarzenia) i wyszukaj zdarzenia o nazwie onCreate. Zdarzenie to pojawia się, gdy dany obiekt jest tworzony, czyli, w przypadku okna głównego, na początku działania aplikacji. Kliknij raz w zdarzenie onCreate, aby je wybrać, następnie kliknij strzałeczkę w dół, aby rozwinąć listę zdefiniowanych już funkcji obsługi zdarzeń. Na liście tej powinna występować funkcja btnCLRClick, która obsługuje kliknięcie przycisku C. Wybierz ją dla zdarzenia onCreate okna.

obrazek

Co zrobiliśmy? Kazaliśmy, aby zdarzenie onCreate dla okna aplikacji obsługiwała funkcja kliknięcia przycisku C. W ten sposób wykorzystujemy już wcześniej utworzony kod do nowych zadań. Gdy teraz skompilujesz i uruchomisz program, kalkulator wystartuje w stanie wyzerowanym, jakbyś kliknął przycisk C.

 

Kolejną czynnością będzie utworzenie dwóch funkcji usługowych, które obsługują stos:

 

push(v) - przesuwa zawartość stosu w górę, a v umieszcza na spodzie.
pop() - pobiera spód stosu i przemieszcza jego zawartość w dół

 

Funkcje te nie obsługują zdarzeń, jednakże muszą posiadać dostęp do komponentów lblS0...lblS4. Dlatego będą funkcjami składowymi klasy naszego okna. Najpierw w definicji klasy umieścimy informację, że klasa takie funkcje posiada. Przejdź do edytora kodu (klawisz F12) i naciśnij Ctrl+F6. Zostaniesz przeniesiony do pliku nagłówkowego Unit1.h. W pliku tym znajduje się definicja klasy okna:

 

class TfrmCalc : public TForm
{
__published:	// IDE-managed Components
        TLabel *lblS4;
        TLabel *lblS3;
        TLabel *lblS2;
        TLabel *lblS1;
        TLabel *lblS0;
        TEdit *edtInput;
        TButton *btnSQR;
        TButton *btnEX;
        TButton *btnDUP;
        TButton *btnDEL;
        TButton *btnENT;
        TButton *btnDIV;
        TButton *btnMUL;
        TButton *btnSUB;
        TButton *btnADD;
        TButton *btnCLR;
        void __fastcall btnCLRClick(TObject *Sender);
private:	// User declarations
public:		// User declarations
        __fastcall TfrmCalc(TComponent* Owner);
};

 

W definicji tej mamy trzy bloki: __published, private i public. Nasze definicje dopisujemy ręcznie w bloku public:

 

class TfrmCalc : public TForm
{
__published:	// IDE-managed Components
        TLabel *lblS4;
        TLabel *lblS3;
        TLabel *lblS2;
        TLabel *lblS1;
        TLabel *lblS0;
        TEdit *edtInput;
        TButton *btnSQR;
        TButton *btnEX;
        TButton *btnDUP;
        TButton *btnDEL;
        TButton *btnENT;
        TButton *btnDIV;
        TButton *btnMUL;
        TButton *btnSUB;
        TButton *btnADD;
        TButton *btnCLR;
        void __fastcall btnCLRClick(TObject *Sender);
private:	// User declarations
public:		// User declarations
        __fastcall TfrmCalc(TComponent* Owner);
        double pop();        // pobiera daną ze stosu
        void push(double v); // umieszcza daną na stosie

};

 

Gdy wpiszesz prototypy funkcji pop() i push() do definicji klasy TfrmCalc, naciśnij Ctrl+F6, aby powrócić do modułu Unit1.cpp. Teraz na końcu pliku dopisz ręcznie definicje tych funkcji:

 

double TfrmCalc::pop()
{
  double v;
  v = StrToFloat(lblS0->Caption);  // odczytujemy ostatni element
  lblS0->Caption = lblS1->Caption; // przesuwamy stos w dół
  lblS1->Caption = lblS2->Caption;
  lblS2->Caption = lblS3->Caption;
  lblS3->Caption = lblS4->Caption;
  lblS4->Caption = "0";            // ostatni element stosu zerujemy
  return v;                        // zwracamy odczytany element
}
//---------------------------------------------------------------------------
void TfrmCalc::push(double v)
{
  lblS4->Caption = lblS3->Caption; // przesuwamy stos w górę
  lblS3->Caption = lblS2->Caption;
  lblS2->Caption = lblS1->Caption;
  lblS1->Caption = lblS0->Caption;
  lblS0->Caption = FloatToStr(v);  // wartość v zapisujemy na spodzie stosu
}
//---------------------------------------------------------------------------

 

Teraz dodamy obsługę przycisku ENT. W tym celu kliknij go dwukrotnie myszką, aby środowisko utworzyło dla niego funkcję obsługującą kliknięcie. Do funkcji tej wpisz kod:

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnENTClick(TObject *Sender)
{
  if(edtInput->Text != "")
  {
    push(StrToFloat(edtInput->Text)); // przesyłamy liczbę na stos
    edtInput->Text = "";              // czyścimy wiersz wejścia
  }
}
//---------------------------------------------------------------------------

 

Jeśli skompilujesz i uruchomisz aplikację, to będziesz już mógł wprowadzać na stos liczby:

obrazek

Zwróć uwagę, że w naszym kalkulatorze stos może przechowywać tylko pięć liczb w etykietach. Jeśli wprowadzisz więcej, to górna liczba będzie utracona.

W podobny sposób tworzysz pozostałe funkcje obsługi kliknięć przycisków. Poniżej podajemy ich kody, jednakże pamiętaj, aby funkcję utworzyć za pomocą mechanizmów środowiska (klikając dwukrotnie w przycisk lub w odpowiednie zdarzenie na zakładce Events w oknie Object Inspector dla danego komponentu). Do edytora przepisujesz tylko treść funkcji. Zwracam na to uwagę, ponieważ uczniowie często zapominają o tej "drobnostce", a później programu nie daje się uruchomić.

 

Dodawanie

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnADDClick(TObject *Sender)
{
  push(pop()+pop());
}
//---------------------------------------------------------------------------

 

Odejmowanie

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnSUBClick(TObject *Sender)
{
  push(-pop()+pop());
}
//---------------------------------------------------------------------------

 

Mnożenie

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnMULClick(TObject *Sender)
{
  push(pop()*pop());
}
//---------------------------------------------------------------------------

 

Dzielenie

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnDIVClick(TObject *Sender)
{
  push(1/pop()*pop());
}
//---------------------------------------------------------------------------

 

Usuwanie

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnDELClick(TObject *Sender)
{
  pop();
}
//---------------------------------------------------------------------------

 

Kopiowanie

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnDUPClick(TObject *Sender)
{
  double v = pop();
  push(v);
  push(v);
}
//---------------------------------------------------------------------------

 

Zamiana

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnEXClick(TObject *Sender)
{
  double x,y;
  x = pop();
  y = pop();
  push(x);
  push(y);
}
//---------------------------------------------------------------------------

 

Pierwiastek

Tutaj na początku pliku Unit1.cpp musisz dołączyć plik nagłówkowy z definicjami funkcji matematycznych za pomocą polecenia #include <math.h>

 

//---------------------------------------------------------------------------
void __fastcall TfrmCalc::btnSQRClick(TObject *Sender)
{
  push(sqrt(pop()));
}
//---------------------------------------------------------------------------

 

Skompiluj i uruchom aplikację. Kalkulator jest gotowy, miłej zabawy. Dla prostoty kodu nie obsługujemy ewentualnych błędów, które mogą się pojawić przy współpracy z użytkownikiem.

W ramach ćwiczeń możesz pododawać do niego różnych funkcji matematycznych, np. SIN, COS, TAN, LN, EXP, itp.

 


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2024 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe