Języki programowania



Podobne dokumenty
Języki programowania

1 Podstawy c++ w pigułce.

1 Podstawy c++ w pigułce.

Część 4 życie programu

Wyjątki (exceptions)

Podstawy Informatyki. Inżynieria Ciepła, I rok. Wykład 10 Kurs C++

I - Microsoft Visual Studio C++

Podstawy Programowania Podstawowa składnia języka C++

Podstawy programowania skrót z wykładów:

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

4. Wyrzuć wyjątek jeśli zmienna ist nie istnieje bloki: try, catch i wyrzucanie wyjątku

Język ludzki kod maszynowy

Programowanie komputerowe. Zajęcia 1

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 15 kwietnia K.Grzelak (Wykład 8) Programowanie w C++ 1 / 33

Pliki wykład 2. Dorota Pylak

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Podstawy programowania. Wykład: 4. Instrukcje sterujące, operatory. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Programowanie w C++ Wykład 9. Katarzyna Grzelak. 14 maja K.Grzelak (Wykład 9) Programowanie w C++ 1 / 30

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Wstęp do informatyki- wykład 11 Funkcje

Wstęp do informatyki- wykład 12 Funkcje (przekazywanie parametrów przez wartość i zmienną)

Wstęp do programowania. Wykład 1

1 Wskaźniki. 1.1 Główne zastosowania wskaźników

Instytut Mechaniki i Inżynierii Obliczeniowej Wydział Mechaniczny Technologiczny Politechnika Śląska

Wstęp do informatyki- wykład 7

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Wstęp do informatyki- wykład 9 Funkcje

Język C++ wykład VIII

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

#include <stdio.h> int main( ) { int x = 10; long y = 20; double s; s = x + y; printf ( %s obliczen %d + %ld = %f, Wynik, x, y, s ); }

Struktura pliku projektu Console Application

Metody Metody, parametry, zwracanie wartości

Podstawy algorytmiki i programowania - wykład 4 C-struktury

Programowanie w C++ Wykład 6. Katarzyna Grzelak. kwiecień K.Grzelak (Wykład 6) Programowanie w C++ 1 / 40

Programowanie - wykład 4

TEMAT : KLASY DZIEDZICZENIE

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

Szablony funkcji i szablony klas

Języki programowania

1. Wypisywanie danych

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 7 maja K.Grzelak (Wykład 8) Programowanie w C++ 1 / 31

Podstawy Informatyki. Kompilacja. Historia. Metalurgia, I rok. Kompilatory C++ Pierwszy program. Dyrektywy preprocesora. Darmowe:

Programowanie komputerowe. Zajęcia 5

Podstawy języka C++ Maciej Trzebiński. Praktyki studenckie na LHC IFJ PAN. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. M. Trzebiński C++ 1/16

Podstawy Informatyki. Metalurgia, I rok. Wykład 6 Krótki kurs C++

Język C++ Różnice między C a C++

Obliczenia, zmienne. Proste działania, zmienne, rodzaje zmiennych, proste operacje i działania na zmiennych.

Wiadomości wstępne Środowisko programistyczne Najważniejsze różnice C/C++ vs Java

Podstawy programowania. Wykład: 9. Łańcuchy znaków. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Programowanie C++ Wykład 2 - podstawy języka C++ dr inż. Jakub Możaryn. Warszawa, Instytut Automatyki i Robotyki

4. Funkcje. Przykłady

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

Wykład I. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Zajęcia nr 1 Podstawy programowania. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Programowanie Obiektowo Zorientowane w języku c++ Przestrzenie nazw

Wykład 2 Składnia języka C# (cz. 1)

Pliki wykład 2. Dorota Pylak

Wykład II Tablice (wstęp) Przykłady algorytmów Wstęp do języka C/C++

utworz tworzącą w pamięci dynamicznej tablicę dwuwymiarową liczb rzeczywistych, a następnie zerującą jej wszystkie elementy,

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Języki programowania C i C++ Wykład: Typy zmiennych c.d. Operatory Funkcje. dr Artur Bartoszewski - Języki C i C++, sem.

C++ Przeładowanie operatorów i wzorce w klasach

Podstawy Programowania

Programowanie obiektowe C++

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 5 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 41

#include <stdio.h> void main(void) { int x = 10; long y = 20; double s; s = x + y; printf ( %s obliczen %d + %ld = %f, Wynik, x, y, s ); }

1 P roste e t ypy p d a d n a ych c - c ąg ą g d a d l a szy 2 T y T py p z ł z o ł żo ż ne e d a d n a ych c : T BLICE

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

Wykład I. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2014 Janusz Słupik

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Podstawy algorytmiki i programowania - wykład 5 C-struktury cd.

Microsoft IT Academy kurs programowania

Efektywna analiza składniowa GBK

Projektowanie klas c.d. Projektowanie klas przykład

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Podstawy programowania, Poniedziałek , 8-10 Projekt, część 1

Języki C i C++ Wykład: 2. Wstęp Instrukcje sterujące. dr Artur Bartoszewski - Języki C i C++, sem. 1I- WYKŁAD

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

1 Wskaźniki i zmienne dynamiczne, instrukcja przed zajęciami

Język C++ zajęcia nr 2

Podstawowe elementy proceduralne w C++ Program i wyjście. Zmienne i arytmetyka. Wskaźniki i tablice. Testy i pętle. Funkcje.

OPERACJE WEJŚCIA / WYJŚCIA. wysyła sformatowane dane do standardowego strumienia wyjściowego (stdout)

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

Operacje wejścia/wyjścia odsłona pierwsza

Analiza leksykalna 1. Języki formalne i automaty. Dr inż. Janusz Majewski Katedra Informatyki

Podstawy programowania - 1

Laboratorium Wstawianie skryptu na stroną: 2. Komentarze: 3. Deklaracja zmiennych

Operatory cd. Relacyjne: ==!= < > <= >= bool b; int i =10, j =20; dzielenie całkowitych wynik jest całkowity! Łączenie tekstu: + string s = "Ala ma ";

Podstawy programowania (1)

Paostwowa Wyższa Szkoła Zawodowa w Płocku Dariusz Wardowski

Instrukcje sterujące mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2012

Programowanie obiektowe

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Analiza leksykalna 1. Teoria kompilacji. Dr inż. Janusz Majewski Katedra Informatyki

Programowanie strukturalne i obiektowe

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Programowanie obiektowe i C++ dla matematyków

Transkrypt:

Języki programowania Nowoczesne techniki programowania Wykład 3 Witold Dyrka witold.dyrka@pwr.wroc.pl 2-16/11/2011

Kolokwium PN, 16.01.2012, godz. 19-21, sala C-13/0.31 Kolokwium poprawkowe pierwszy tydzień sesji

Prawa autorskie Slajdy do wykładu powstały w oparciu o slajdy Bjarne Stroustrupa do kursu Foundations of Engineering II (C++) prowadzonego w Texas A&M University http://www.stroustrup.com/programming

Materiały Literatura Bjarne Stroustrup. Programowanie: Teoria i praktyka z wykorzystaniem C++. Helion (2010) Jerzy Grębosz. Symfonia C++ standard. Edition 2000 (2008) Dowolny podręcznik języka C++ w standardzie ISO 98 www.cplusplus.com - The C++ Reference Network (j.ang.) Środowisko programistyczne Microsoft Visual C++ (rekomendowane) Dowolne środowisko korzystające z GCC

Program wykładów 1. Pierwszy program. Obiekty, typy i wartości 2. Wykonywanie obliczeń. Błędy 3. Pisanie i wykańczanie programu 4. Techniki pisania funkcji i klas 5. Strumienie wejścia/wyjścia 6. Wskaźniki i tablice 7. Systematyka technik i języków programowania

Plan na dziś O tworzeniu oprogramowania Pomysł na kalkulator Zastosowanie gramatyki Obliczanie wyrażeń Organizacja programu Tokeny i strumienie tokenów Czyszczenie kodu

Tworzenie programu Analiza Lepsze rozumienie problemu z myślą o docelowym zastosowaniu programu Projekt Ogólna struktura programu Implementacja Kodowanie Debugowanie Testowanie Powtarzanie etapów aż do osiągnięcia celu

Strategia pisania programu Jaki problem chcesz rozwiązać? Czy problem został jasno postawiony? Czy problem wydaje się do rozwiązania, biorąc pod uwagę czas, umiejętności i dostępne narzędzia? Podziel problem na dające się ogarnąć części Czy znasz jakieś narzędzia, biblioteki itp., które mogą być przydatne? (np. iostreams, vector) Stwórz niewielką, ograniczoną wersję programu, rozwiązującą najważniejszą część problemu Zauważysz braki w rozumieniu, pomysłach, narzędziach Prawdopodobnie zmodyfikujesz specyfikację problemu Jeśli taki prototyp nie działa lub jest bardzo brzydki Wyrzuć go i stwórz nowy prototyp powtarzaj do skutku Stwórz pełną wersję programu Najlepiej wykorzystując części prototypu

Pisanie programu przykład Będziemy popełniali typowe błędy Nawet doświadczeni programiści robią mnóstwo błędów Projektowanie programu jest trudne Skupimy się na sensownym projekcie Wykrywanie większości błędów zostawimy kompilatorowi Zbudujemy pierwszą, niekompletną wersję To pozwoli przetestować pomysł Dobry program trzeba wyhodować od małego:-)

Prosty kalkulator Oblicza wyrażenia wprowadzane z klawiatury i wyświetla wyniki, np. Wyrażenie: 2+2 Wynik: 4 Wyrażenie: 2+2*3 Wynik: 8 Wyrażenie: 2+3-25/5 Wynik: 0 Ujmijmy to w pseudokodzie...

Pseudokod prostego kalkulatora Pierwszy pomysł: int main() { zmienne dopóki (dostajesz linię) { // pseudokod // a co to jest linia? analizuj wyrażenie oblicz wyrażenie wyświetl wynik // co to znaczy? Jak przedstawić wyrażenie 45+5/7 jako dane programu? Jak znaleźć czynniki 45 + 5 / oraz 7 w łańcuchu wejściowym? Jak zapewnić, żeby 45+5/7 znaczyło 45+(5/7), a nie (45+5)/7? Czy należy dopuścić liczby zmiennoprzecinkowe? (jasne!) Czy należy dopuścić tworzenie zmiennych? v=7; m=9; v*m (nie od razu)

Prosty kalkulator Czekaj! Czy nie probujemy wynaleźć koła? Programy do wyliczania wyrażeń są tworzone od 50 lat Istnieją gotowe rozwiązania! Co proponują specjaliści? Poczytaj, sprawdź! Poczytać i popytać bardziej doświadczonych kolegów będzie przeważnie bardziej efektywne, przyjemne i szybsze niż mozolne kombinowanie po swojemu

Gramatyka wyrażeń arytmetycznych Typowym rozwiązaniem problemu kalkulatora wyrażeń jest gramatyka: Expression : // wyrażenie Term Expression + Term // np. 1+2, (1-2)+3, 2*3+1 Expression - Term Term : // składnik Primary Term * Primary // np. 1*2, (1-2)*3.5 Term / Primary Term % Primary Primary : // czynnik Number // np. 1, 3.5 ( Expression ) // np. (1+2*3) Number : literał zmiennoprzecinkowy // liczba // np. 3.14, 0.274e1 lub 42 as defined for C++ Wyrażenie składa się z tokenów (liczb i operatorów) są one jakby słownikiem kalkulatora

O gramatykach Czym jest gramatyka? Zbiorem reguł, jak analizować (parsować) wyrażenie (zdanie) składające się z tokenów (symboli terminalnych), np. liczba, operator lub wyraz w jęz.naturalnym. W toku analizy składni wprowadzamy zmienne syntaktyczne (symbole nieterminalne) wyrażające konstrukcje wyższego rzędu, np.: czynnik, wyrażenie lub rzeczownik, fraza czasownikowa. Co ciekawe, badania psycholingwistyczne wskazują, że ludzi rodzą się z pewnym gotowym zestawem reguł w umyśle, zwanym gramatyką generatywną: Wiemy, że Ptaki latają, a ryby pływają to zdanie poprawne A zdanie Latają ptaki ryby, ale pływają ma błędną składnię Podobnie, wyrażenie arytmetyczne 2*3+4/2 jest poprawne Natomiast: 2 * + 3 4/2 nie jest poprawne. Dlaczego? Skąd wiemy? Jak nauczyć komputer, tego co wiemy?

Gramatyka język angielski Stroustrup/Programming 15

Gramatyka wyrażeń arytmetycznych Wyrażenie Składnik Czynnik Liczba Stroustrup/Programming 16

Gramatyka wyrażeń (2) Stroustrup/Programming 17

Gramatyka wyrażeń (3) Stroustrup/Programming 18

Zabieramy się za projektowanie: funkcje Stworzymy parser program przetwarzający zadane wyrażenie przy użyciu gramatyki. Jak? Najprościej! Napiszemy osobną funkcję dla każdej zmiennej syntaktycznej oraz funkcję do pobierania tokenów: get() // wczytuje znaki i tworzy tokeny // wywołuje cin do czytania z wejścia expression() // obsługuje operacje + i // wywołuje term() i get() term () // obsługuje operacje *, / i % // wywołuje primary() i get() primary() // obsługuje operacje liczby i nawiasy // wywołuje expression() i get() Zauważmy: Każda funkcja zajmuje się tylko określoną częścią wyrażenia i resztę pozostawia innym funkcjom. To radykalnie upraszcza kod funkcji Analogia: Zespół specjalistów, w którym każdy zajmuje się tylko tym, na czym się zna najlepiej i przekazuje wynik reszcie

Projektowanie: typy zwracane Token get() // wczytuje znaki i tworzy tokeny // zwraca token (obiekt typy Token) double expression() // obsługuje operacje + i // zwraca sumę lub różnicę wartość typu zmiennoprzecinkowego double term () // obsługuje operacje *, / i % // zwraca iloczyn, iloraz lub modulo double primary() // obsługuje operacje liczby i nawiasy // zwraca wartość czynnika Uwaga: funkcje nie pobierają argumentów, ponieważ zakładamy, że wczytują kolejny token z wejścia Czym jest Token?

liczba 4.5 Czym jest Token? + Na wejściu danych z klawiatury dostajemy strumień znaków np. 1 + 4*(4.5-6) składa się z 13 znaków, w tym 2 spacji Ale słownik naszej gramatyki wyrażeń nie składa się z liter i cyfr, tylko z liczb i operatorów, czyli tokenów: mamy tutaj 9 tokenów: 1 + 4 * ( 4.5-6 ) w sumie 6 rodzajów tokenów: liczba + * ( - ) Token reprezentuje pewną całość, np. liczbę lub operator. W tym celu potrzebuje: rodzaju (kind), np. liczba wartości (value), np. 4 Ideę tokena będziemy reprezentowali przez nowy typ Token Opiszemy go później, na teraz ważne jest, że: Funkcja get_token() daje nam kolejny token na wejściu t.kind daje rodzaj tokena t, a t.value daje nam wartość tokena t

expression() obsługa + i Expression: Term Expression + Term Expression - Term // Zauważ: każde wyrażenie zaczyna się składnikiem (Term) double expression() // wczytuje i oblicza: 1 1+2.5 1+2+3.14 itp. { double left = term(); // wczytuje rozpoczynający składnik (term) while (true) { Token t = get_token(); // wczytuje kolejny token... switch (t.kind) { // i robi z nim co trzeba case '+': left += term(); break; case '-': left -= term(); break; default: return left; // zwraca wartość wyrażenia

term() obsługa *, / i % double term() { // wczytuje i oblicza wartość składnika (Term) // dokładnie jak dla wyrażenia double left = primary(); // wczytuje czynnik (Primary) while (true) { Token t = get_token(); // wczytuje kolejny token... switch (t.kind) { case '*': left *= primary(); break; case '/': left /= primary(); break; case '%': left %= primary(); break; default: return left; // zwraca wartość Ale... to się nie kompiluje! Operacja modulo nie jest zdefiniowana dla typu zmiennoprzecinkowego (patrz wykład 1)

term() obsługa * i / poprawione Term : Primary Term * Primary Term / Primary // Zauważ: każdy składnik zaczyna się czynnikiem (Primary) double term() // wczytuje i oblicza wartość składnika (Term) { double left = primary(); // wczytuje czynnik (Primary) while (true) { Token t = get_token(); // wczytuje kolejny token... switch (t.kind) { case '*': left *= primary(); break; case '/': left /= primary(); break; default: return left; // zwraca wartość

term() obsługa * i / obsługa dzielenia przez zero double term() // wczytuje i oblicza wartość składnika (Term) { double left = primary(); // wczytuje czynnik (Primary) while (true) { Token t = get_token(); // wczytuje kolejny token... switch (t.kind) { case '*': left *= primary(); break; case '/': { double d = primary(); if (d==0) throw runtime_error("dzielenie przez zero"); left /= d; break; default: return left; // zwraca wartość

primary() obsługa liczb i nawiasów double primary() // liczba lub ( wyrażenie ) { Token t = get_token(); switch (t.kind) { case '(': // obsługuje ( wyrażenie ) { double d = expression(); t = get_token(); if (t.kind!= ')') throw runtime_error("oczekiwano ')'"); return d; case '8': // rodzaj tokenu liczba oznaczamy znakiem 8 return t.value; // zwraca wartośc liczby default: throw runtime_error("oczekiwano składnika");

Zależności pomiędzy funkcjami double expression() term() expression + - term() pobiera operator double term() pobiera operator Token get_token() primary() term * / primary() pobiera nawias lub liczbę double primary() ( expression() )

Program #include<stdexcept> // standardowa biblioteka wyjątków (exceptions) #include<iostream> // standardowa biblioteka strumieni wejścia-wyjścia(i/o stream) using namespace std; // standardowa przestrzeń nazw // Tu umieścimy kod odpowiadający za obsługę tokenów double expression(); // deklaracja potrzebna, aby primary() mogło wywołać expression() double primary() { /* */ // obsługa liczb i nawiasów double term() { /* */ // obsługa mnożenia i dzielenia (problem z modulo) double expression() { /* */ // obsługa dodawania i odejmowania int main() { /* */ // kod na następnym slajdzie

Program funkcja main() int main() try { while (cin) cout << expression() << '\n'; system( pause ); // zatrzymuje okno po zakończeniu programu // potrzebne w niektórych wersjach Windows catch (runtime_error& e) { // obsługa wyjątków typu runtime_error cerr << e.what() << endl; system( pause ); return 1; // zwraca kod błędu 1 catch ( ) { cerr << "exception \n"; system( pause ); return 2; // obsługa pozostałych wyjątków

Pierwsza próba... Hmm... 2 wejście wejście 3 wejście 4 wejście 2 WYJŚCIE (zwrócił czynnik, ale z opóźnieniem) 5+6 wejście WYJŚCIE (zwrócił tylko pierwszy czynnik) 5 X wejście zly token WYJŚCIE (ok, tego się spodziewaliśmy) Aby kontynuować, naciśnij dowolny klawisz...

Testujemy dalej... 1 2 3 4+5 6+7 8+9 10 11 12 1 4 6 WYJŚCIE 8 10 Ha, wygląda na to, że program zjada 2 na 3 tokeny Jak to możliwe? Popatrzmy na funkcję expression(), która jest wywoływana jako pierwsza

expression() obsługa + i Expression: Term Expression + Term Expression - Term // Zauważ: każde wyrażenie zaczyna się składnikiem (Term) double expression() // wczytuje i oblicza: 1 1+2.5 1+2+3.14 itp. { double left = term(); // wczytuje rozpoczynający składnik (term) while (true) { Token t = get_token(); // wczytuje kolejny token... switch (t.kind) { // i robi z nim co trzeba case '+': left += term(); break; case '-': left -= term(); break; default: return left; // token zostaje zjedzony: został pobrany, // nie został użyty i nie został zwrócony

expression() obsługa + i Musimy odłożyć nieużyty token z powrotem double expression() // wczytuje i oblicza: 1 1+2.5 1+2+3.14 itp. { double left = term(); // wczytuje rozpoczynający składnik (term) while (true) { Token t = get_token(); // wczytuje kolejny token... switch (t.kind) { // i robi z nim co trzeba case '+': left += term(); break; case '-': left -= term(); break; default: putback_token(t); return left; Ale gdzie odłożyć? Problem: do standardowego strumienia wejścia można odkładać tylko pojedyńcze znaki, to niewygodne. Przydałby się strumień tokenów

'+' Typ użytkownika: Token '8' 2.3 struct Token { char kind; double value; // ; // definiuje typ o nazwie Token // Token składa się on z rodzaju oraz wartości: // rodzaj tokena // wartość tokena (używana tylko dla rodzaju liczba) Token t; t.kind = '8'; t.value = 2.3; Token u = t; // deklaracja zmiennej t typu Token //. (kropka) jest używana do dostępu do składowych typu // (używamy 8 jako oznaczenie typu liczba ) // typ Token zachowuje się podobnie jak typy wbudowane, // np. int. Tutaj u staje się kopią t cout << u.value; // ta instrukcja wydrukuje 2.3

Typy użytkownika: struktury i klasy struct Token { // typ użytkownika o nazwie Token // dane składowe // funkcje składowe class Token { // typ użytkownika o nazwie Token ; // dane składowe // funkcje składowe ; W C++ typ użytkownika nazywamy klasą (class) Klasy mogą mieć dwie bliźniacze formy: struct i class o różnicy między nimi powiemy na następnym wykładzie. Definiowanie typów użytkownika jest kluczowym mechanizmem dostępnym w większości współczesnych języków programowania Klasy mają dwa typy składowych: dane przechowujące informacje funkcje zapewniające operacje na danych

Inicjowanie obiektu typu użytkownika konstruktor Tokena struct Token { char kind; double value; ; // rodzaj tokena // dla liczb: wartość Token(char ch) : kind(ch), value(0) { Token(char ch, double val) : kind(ch), value(val) { // konstruktor // konstruktor Tworząc własny typ danych potrzebujemy określić sposób inicjowania obiektów tego typu: służy do tego konstruktor Konstruktor zawsze nazywa się tak samo jak jego klasa W przypadku Tokena, kind jest inicjowany przez ch, a value przez val lub 0: Token('+'); // tworzy Token rodzaju '+' Token('8',4.5); // tworzy Token rodzaju '8' i wartości 4.5

Strumień tokenów: klasa Token_stream Czego potrzebujemy do obsługi tokenów? Funkcja 1: Wczytywanie znaków z wejścia i tworzenie tokenów Funkcja 2: Odkładanie tokenu na później W tym celu potrzebujemy bufora, w którym będziemy przechowywać tokeny strumień WE: 1+2*3; bufor: pusty Dla 1+2*3;, expression() wywołuje term(), który wczyta 1, a następnie '+', po czym zdecyduje, że '+' to robota dla kogoś innego i odłoży Token('+') do strumienia tokenów (tam znajdzie go expression()) strumień WE: bufor: 2*3; Token('+')

Projekt klasy Token_stream Funkcja 1: Wczytywanie znaków z wejścia i tworzenie tokenów Funkcja 2: Odkładanie tokenu na później Składowa: Bufor, w którym będziemy przechowywać odłożony token class Token_stream { public: // interfejs użytkownika klasy: Token get(); // pobierz Token (funkcja 1) void putback(token); // odłóż Token do strumienia Token_stream (fun.2) Token_stream(); // konstructor: tworzy Token_stream private: // składowe klasy nie są bezpośrednio dostępne użytkownikowi klasy Token buffer; // bufor przechowujący Token odłożony przy użyciu putback() bool full; // czy w buforze jest Token? ; Po co oddzielać interfejs klasy (public) od szczegółów implementacyjnych (private)?

Implementacja metod Token_stream Konstruktor - definiuje sposób inicjacji obiektu klasy - ma taką samą nazwę jak klasa i nie ma typu zwrotnego Token_stream::Token_stream() :full(false), buffer(0) { Metoda odkładania tokena void Token_stream::putback(Token t) { // bufor jest pusty if (full) throw runtime_error("odkładanie do pełnego bufora"); buffer=t; full=true;

Token Token_stream::get() // wczytuje Token ze strumienia tokenów { if (full) { full=false; return buffer; // sprawdza, czy w buforze jest token Implementacja Token_stream (2) char ch; cin >> ch; // zauważ, że >> pomija białe znaki (spacja, nowa linia, tabulator itp.) switch (ch) { case '(': case ')': case '+': case '-': case '*': case '/': return Token(ch); // każdy znak reprezentuje sam siebie case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { cin.putback(ch); // wstawia cyfrę z powrotem do strumienia wejściowego double val; cin >> val; // wczytuje liczbę zmiennoprzecinkową return Token('8',val); // 8 oznacza liczbę default: runtime_error("zly token");

Strumienie Koncepcja strumienia danych jest bardzo ogólna i szeroko stosowana w programowaniu w większości systemów wejścia-wyjścia np. strumień WE/WY w C++ z metodą odkładania do strumienia lub bez niej zwyczajowo nazywaną putback() lub unget() Gdzie byłoby trudno zaimplementować tę metodę? Zwróć uwagę, że nasza klasa Token_stream implementuje w pewnym zakresie strumień tokenów, ale nim nie jest (cokolwiek miałoby to znaczyć) Pomyśl nad alternatywną implementacją strumienia w oparciu o kontener vector<token>

Teraz możemy poprawić program: expression() obsługa + i Musimy odłożyć nieużyty token z powrotem double expression() // wczytuje i oblicza: 1 1+2.5 1+2+3.14 itp. { double left = term(); // wczytuje rozpoczynający składnik (term) while (true) { Token t = ts.get(); // wczytuje kolejny token ze strumienia tokenów ts... switch (t.kind) { // i robi z nim co trzeba case '+': left += term(); break; case '-': left -= term(); break; default: ts.putback(t); // odkłada token t do strumienia tokenów ts... return left; Analogiczne zmiany wprowadzamy w funkcji term(). Zmieniamy także funkcję get_token() na metodę ts.get() w primary().

Uff... Jakoś działa:-) 1 2 3 4+5 6+7 8+9 10 11 12 1 2 3 9 13 17 10 11 Całkiem nieźle nam poszło. Ale jakoś działa to za mało! W zasadzie dopiero teraz zaczyna się praca i zabawa:-) Od działającego programu dostaniemy informacje zwrotne Ale... Gdzie podziała się 12?

Kolejny test potwierdza... 2 3 4 2+3 2*3 2 3 4 5 Nasz program wczytuje jeden token do przodu Najłatwiej będzie rozwiązać problem dodając instrukcję drukowania na ekranie, np. ';' Przy okazji dodamy instrukcję wyjścia z programu, np. 'q'

Nowa wersja funkcji main() int main() try { double val = 0; while (cin) { Token t = ts.get(); if (t.kind == 'q') break; // q jak quit if (t.kind == ';') // ; oznacza drukuj teraz cout << val << '\n'; // drukuj wynik else { ts. putback(t); // odłóż token do strumienia tokenów val = expression(); // oblicz system(''pause''); // obsługa wyjątków

Nowa wersja Token_stream::get() Token Token_stream::get() { //... switch (ch) { case '(': case ')': case ';': case 'q': case '+': case '-': case '*': case '/': return Token(ch); //... Sukces!! 2; 3; 4; 2+3; 2*3; q 2 3 4 5 6 Aby kontynuować, naciśnij dowolny klawisz...

Program jest wciąż prymitywny (1) Poprawimy go w kolejnych etapach rozwoju Funkcjonalność Lepszy interfejs użytkownika kalkulatora Odzyskiwanie sprawności po błędach Liczby ujemne % (reszta z dzielenia/modulo) Predefiniowane wartości symboliczne (np. pi, e itp.) Zmienne

Interfejs użytkownika Początkowo chcieliśmy czegoś takiego: Wyrażenie: 2+3; 5*7; 2+9; Wynik : 5 Wyrażenie: Wynik: 35 Wyrażenie: Wynik: 11 Wyrażenie: Ale zaimplementowaliśmy to tak: 2+3; 5*7; 2+9; 5 35 11 Czego naprawdę potrzebujemy? Może tak? > 2+3; = 5 > 5*7; = 35 >

Wersja main() z interfejsem int main() try { double val = 0; cout << "> "; // drukuje zaproszenie while (cin) { Token t = ts.get(); if (t.kind == 'q') break; // q jak quit if (t.kind == ';') // ; oznacza drukuj teraz cout << "= " << val << "\n> "; // drukuje = wynik i zaproszenie else { ts. putback(t); // odłóż token do strumienia tokenów val = expression(); // oblicz system("pause"); // obsługa wyjątków

Drobne poprawki int main() try { Problem: wpisanie >1+2;;; wyświetla trzy razy = 3. Sprawdź Poprawka i ulepszenie struktury kodu: cout << "> "; while (cin) { Token t = ts.get(); while (t.kind == ';') t=ts.get(); // zjedz wszystkie ; if (t.kind == 'q') { system("pause"); return 0; ts.putback(t); // skończył jeść ';' na nie-';', niech go zwróci cout << "= " << expression() << "\n > "; // obsługa wyjątków

Program jest wciąż prymitywny (2) Poprawimy go w kolejnych etapach rozwoju Styl jasność kodu Przeczytaj dokładnie i systematycznie cały kod Popraw komentarze (czy są wciąż adekwatne, czy ktoś inny je zrozumie, czy są zwięzłe?) Zmień niejasne nazwy klas, zmiennych i funkcji na znaczące Ulepsz użycie funkcji (czy wciąż odzwierciedlają strukturę programu? Czy nie mieszamy logicznie rozdzielnych zadań patrz nasza funkcja main()) Pozbądź się magicznych stałych (np. '8' w naszym kodzie) Daj sprawdzić kod koledze

Definiowanie stałych symbolicznych // Rodzaje tokenów: const char number = '8'; const char quit = 'q'; const char print = ';'; // liczba zmiennoprzecinkowa // polecenie zakończenia // polecenie drukownia // Łańcuchy interfejsu użytkownika: // Uwaga: dołącz bibliotekę string const string prompt = "> "; const string result = "= ";

Usuwanie stałych magicznych (1) // W Token_stream::get(): case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { cin.putback(ch); double val; cin >> val; return Token(number,val); // zamiast Token('8',val) // W primary(): case number: return t.value; // zamiast case '8':

Usuwanie stałych magicznych (2) // W main(): while (cin) { cout << prompt; // zamiast "> " Token t = ts.get(); while (t.kind ==print) t=ts.get(); // zamiast ==';' if (t.kind == quit) { system("pause"); return 0; ts.putback(t); cout << result << expression() << endl; // zamiast =='q'

O stałych magicznych Wbrew pozorom ludzie nie wszyscy muszą pamiętać, co znaczą np. 3.14159265358979323846264, 12, -1, 365, 24, 2.7182818284590, 299792458, 2.54, 1.61, -273.15, 6.6260693e-34, 0.5291772108e-10, 6.0221415e23 oraz 42! Ponoć stałe magiczne powodowały już katastrofy statków kosmicznych Na pewno powodują niewyspanie Jeżeli stała może ulec zmianie w czasie pielęgnacji programu lub ktoś może jej nie rozpoznać użyj stałej symbolicznej Np. zmiana precyzji ma znaczenie: 3.14!= 3.14159265 0 i 1 są z reguły dobre bez wyjaśnień, -1 i 2 tylko czasami (ale rzadko) 12 może być w porządku jako liczba miesięcy, poza tym raczej nie Zasada: jeżeli stała jest używana co najmniej 2 razy prawdopodobnie trzeba użyć stałej symbolicznej W razie zmiany jej wartości wystarczy zrobić je w jednym miejscu

Dziś najważniejsze było to, że... Nie wymyślamy koła szukamy istniejących rozwiązań problemów Dzielenie programu na logicznie odrębne części, czyli funkcje bardzo ułatwia pracę Definiowanie typów użytkownika jest kluczową techniką nowoczesnego programowania Definiowanie typu zaczynamy od analizy potrzeb (tzw. przypadków użycia) Gdy program jakoś działa to dopiero początek zabawy:-)

Ważne Język programowania jak każdy język ma swoją gramatykę Parsowaniem naszych programów zajmuje się kompilator Dziś stworzyliśmy nie tylko prosty kalkulator, ale i prosty parser interpreter języka wyrażeń W przyszłym semestrze będziecie Państwo pisać programy w nieco bardziej dla nieco bardziej rozwiniętego interpretera wyrażeń matematycznych MATLABa :-)

A za 2 tygodnie... Techniki pisania funkcji i klas