Podstawy programowania Doskonalimy umiejętności
Łyżwiarstwo Mamy napisać program obliczający średnią ocenę w łyżwiarstwie figurowym W uproszczeniu: jest dziewięciu sędziów przyznających noty za wartość techniczną i artystyczną (skala od 0.0 do 6.0) Ocena to średnia poszczególnych not. Wygrywa osoba mająca wyższą średnią.
Proste?
Rozwiązanie nr 1
Łyżwiarstwo Ale co będzie, jeśli będziemy chcieli usuwać najsłabszą i najlepszą ocenę?
Rozwiązanie 2 Wprowadzamy max oraz min Liczymy max, min oraz inaczej średnią.
Łyżwiarstwo Życie płata figle po raz kolejny. Teraz mamy jeszcze sortować oceny od najmniejszej do największej.
Fragment kodu To jest tak zwany kod spaghetti Robimy 10 oddzielnych zmiennych dla każdego sędziego.
Fragment kodu To jest tak zwany kod spaghetti Czytamy każdego oddzielnie.
Fragment kodu To jest tak zwany kod spaghetti A potem sprawdzamy, który z nich jest najmniejszy, a który największy.
Ewaluacja rozwiązania nr 2 Nie dość, że mamy bardzo dużo pisania, to jeszcze kod bardzo mało różni się od siebie, a jest kopiowany w wielu miejscach. Rozwiązanie jest bardzo nieefektywne. Jakakolwiek zmiana prowadzi do zawalenia się kodu jak domek z kart.
Znowu zmiana przepisów A teraz będziemy mieć 100 sędziów!
Znowu zmiana przepisów
Tablica zmiennych jako rozwiązanie 1.5 4.5 2.7 3.3 5.0 3.7 2.2 6.0 6.0 0 1 2 3 4 5 6 7 8 Tablica (zwana również zmienną tablicową) złożona zmienna, będąca kolekcją ponumerowanych elementów tego samego typu. Innymi słowy jest to ciąg zmiennych tego samego typ Ciąg taki posiada swoją nazwę, typ wartości jaki przechowuje, a do jego poszczególnych elementów odwołujemy się przez podanie ich numeru. Numer ten zwany jest indeksem.
Deklaracja tablicy ma z reguły postać: typ nazwa_tablicy[ rozmiar ]; Przykładowa deklaracja: float Dni_tygodnia[7]; Typ elementów Nazwa tablicy Liczba elementów Deklaracja i użycie tablic
Tablice są ZAWSZE indeksowane od zera (0)!
Kilka słów o deklaracji i wykorzystaniu tablic
Ulepszamy nasz program Tablica liczb całkowitych (int) o rozmiarze ilesedziow. Używanie stałych (const) jest wysoce zalecane. Wyobraź sobie co by było gdyby nagle zmieniła się liczba sędziów.
Ulepszamy nasz program Komputer jest dobry w wielokrotnym, szybkim wykonywaniu tych samych instrukcji. No to niech robi
Ulepszamy nasz program Jeden malutki problem normalni ludzie liczą od 1, a nie od 0.
Ulepszamy nasz program No to liczmy po ludzku od 1 zwróć uwagę na głowę pętli.
Ulepszamy nasz program Wszystko wygląda ok
Ulepszamy nasz program Ale po drugim uruchomieniu programu (bez zmiany linijki kodu!) już nie jest ok.
Tablice są ZAWSZE indeksowane od zera (0)!
Ulepszamy nasz program Naprawdę poprawne rozwiązanie.
Ogólny schemat korzystania z tablic for(int i=0; i < rozmiartablicy;i++) { //jakieś operacje } Do iteracyjnego przetwarzania tablic potrzebna jest zmienna indeksowa. Jest to zmienna całkowita, nazwa jest dowolna, najczęściej stosuje się jednak zmienne o nazwach i, j, k, l, m, n,. Najlepiej gdy rozmiar tablicy jest określony nazwaną stałą wtedy łatwiej jest modyfikować rozmiar tablicy bez potencjalnie wielu zmian w programie.
Gdzie deklarować licznik? for(int i=0; i < rozmiartablicy;i++) { //jakieś operacje } int i=0; for(i=0; i < rozmiartablicy;i++) { //jakieś operacje }
Gdzie deklarować licznik? for(int i=0; i < rozmiartablicy;i++) { //jakieś operacje } Wartość licznika niedostępna poza pętlą. int i=0; for(i=0; i < rozmiartablicy;i++) { //jakieś operacje } Wartość licznika dostępna poza pętlą.
Mała dygresja Tablice też należy zerować przed użyciem (odczytem).
Mała dygresja Tak się nie da zerować tablicy!
Poprawny sposób zerowania tablicy Używamy do tego pętli!
Wracamy do łyżwiarstwa Hola! A gdzie to sortowanie? I gdzie te min/max?
Wracamy do łyżwiarstwa Takie (uporządkuj/posortuj tablice) i podobne problemy są bardzo dobrze znane w informatyce. Więc po co wynajdować koło od nowa?
Wracamy do łyżwiarstwa Istnieją proste i intuicyjnie zrozumiałe algorytmy sortowania te nie są zwykle efektywne. Efektywne algorytmy sortowania stosują sprytne choć nie od razu zrozumiałe rozwiązania. Do sortowania niewielkich tablic wystarczą proste algorytmy sortowania nie strzelamy a armaty do wróbelka :)
Pamiętacie algorytm min/max? Algorytm wyznaczania max jest następujący: 1. Za aktualne maksimum przypisz jakąś małą liczbę. 2. Porównuj kolejne liczby z aktualnym maksimum. 3. Jeśli liczba jest większa, to zamień aktualne maksimum.
Algorytm sortowania 1-3 5 15 0 To inaczej wielokrotne wyszukiwanie elementu minimalnego, a następnie przepisywanie tego elementu w nowe miejsce.
Algorytm sortowania 1-3 5 15 0 MIN: 100
Algorytm sortowania 1-3 5 15 0 1 <? MIN MIN: 100
Algorytm sortowania 1-3 5 15 0 1 < MIN MIN: 1
Algorytm sortowania --- -3 5 15 0-3 <? 1 MIN: 1
Algorytm sortowania -- 1 5 15 0 5 >? 1 MIN: -3
Algorytm sortowania -- 1 5 15 0-3 < 1 MIN: -3
Algorytm sortowania -- 1 5 15 0 15 >? -3 MIN: -3
Algorytm sortowania -- 1 5 15 0 0 >? -3 MIN: -3
Algorytm sortowania -- 1 5 15 0 Tablica elementów posortowanych: -3
Algorytm sortowania -- 1 5 15 -- Tablica elementów posortowanych: -3 0
Algorytm sortowania -- -- 5 15 -- Tablica elementów posortowanych: -3 0 1
Algorytm sortowania -- -- -- 15 -- Tablica elementów posortowanych: -3 0 1 5
Algorytm sortowania -- -- -- -- -- Tablica elementów posortowanych: -3 0 1 5 15
Algorytm sortowania Ale jest kilka problemów z tym algorytmem: Potrzeba kolejnej tablicy, aby przechowywać wartości posortowane. Czasami sortujemy duuuuuuże tablice (~miliard rekordów to jeszcze nie tak dużo), stąd pamięci może już braknąć. Jak oznaczać elementy przeniesione do tablicy? Nie mogą być kreski, bo tablica składa się z liczb. Nie da się zostawić pustego miejsca. To może jakaś liczba? Ale jaka? A jak będzie w zestawie danych do posortowania?
Algorytm sortowania w miejscu 1-3 5 15 0 Tradycyjnie, wybieramy element minimalny
Algorytm sortowania w miejscu 1-3 5 15 0
Algorytm sortowania w miejscu 1-3 5 15 0 Zamieniamy dwa elementy w miejscu
Algorytm sortowania w miejscu -3 1 5 15 0 Sortujemy pozostałą część tablicy
Algorytm sortowania w miejscu -3 1 5 15 0 Sortujemy pozostałą część tablicy
Algorytm sortowania w miejscu -3 1 5 15 0 Sortujemy pozostałą część tablicy
Algorytm sortowania w miejscu -3 0 5 15 1 Sortujemy pozostałą część tablicy
Algorytm sortowania w miejscu -3 0 5 15 1 Sortujemy pozostałą część tablicy
Algorytm sortowania w miejscu -3 0 1 15 5 Sortujemy pozostałą część tablicy
Algorytm sortowania w miejscu -3 0 1 15 5 Sortujemy pozostałą część tablicy
Algorytm sortowania w miejscu -3 0 1 5 15 Sortujemy pozostałą część tablicy
Ostatni problem jak zamienić liczby w miejscu? Mniejsza Większa Temp
A jak to zapisać w C++? j = i
Min i max w posortowanej tablicy Min jest w ocenysedziow[0], Max jest w ocenysedziow[ilesedziow-1]
Kolejna mała dygresja C++ został stworzony jako język pierońsko szybki. I wydajny. Stąd kompilator dba o efektywność i ostrzega przed trzema niepotrzebnymi zmiennymi. Usunięcie ich to jakieś 12 B oszczędności pamięci
Istnieją tablice o liczbie wymiarów większej niż 1. Deklaracja tablicy dwuwymiarowej: int plansza[10][10]; Typ elementów Nazwa tablicy Liczba elementów w poszczególnych wymiarach Zwiększamy poziom abstrakcji
Istnieją tablice o liczbie wymiarów większej niż 1. Deklaracja tablicy dwuwymiarowej: int plansza[10][10]; Odwołanie do takiej tablicy jest intuicyjne: plansza[2][3]=1; Zwiększamy poziom abstrakcji
Załóżmy, że chcemy przechować wartość tabliczki mnożenia w tablicy i wyświetlić ją na ekran
Zaprogramuj grę w statki. Zakładamy, że jedyne statki na planszy to jednnomasztowce Na początku użytkownik określa współrzędne swoich statków (1-jest statek, 0-pusto). Jest ich dokładnie 3. Wyświetlamy planszę użytkownikowi, a ten trafia Ćwiczenie
Realizacja
Realizacja
Podprogramy Czyli zaczynamy (mądre słówko) refaktoryzację i suszymy kod.
Taki prosty programik
Taki prosty programik Jak go ulepszyć? Co będzie jak tych tablic będzie 100? A co będzie jak będą to różne tablice? A jak byśmy tak chcieli sobie ułatwić życie?
Dobry programista, to automatyzujący programista
Funkcja w matematyce, to takie coś: f x = x 2 + 5 Funkcje i procedury
Funkcja w matematyce, to takie coś: f x = x 2 + 5 Ale tym Was męczyć nie będę Funkcje i procedury
Funkcja w matematyce, to takie coś: f x = x 2 + 5 Jak działa? Jak się nazywa? Co potrzebuje, żeby działać? Funkcje i procedury
Co zwraca? Jak się nazywa? Co potrzebuje, żeby działać? Jak działa? Funkcje w C++
Funkcje w C++
Gdzie deklarować funkcje? Pytanie nie ma jednej i prostej odpowiedzi, ale najlepiej przed ich wywołaniem. Dzięki temu kompilator najpierw wie jak dana funkcja ma działać, a dopiero potem jej używa. Faktycznie, w większych programach używa się tzw. plików nagłówkowych, ale to nie ta bajka
Gdzie deklarować funkcje? #include <cstdlib> void przywitajsie() { cout << Hello, user! << endl; } int main() { przywitajsie(); ( ) return 0; }
Przypomnijmy sobie matematykę Jest sobie coś takiego jak trójmian kwadratowy. Czyli równanie postaci: Ax 2 + Bx + C = 0 Twoim zadaniem jest napisanie funkcji, która oblicza: Liczbę miejsc zerowych Wartość miejsc zerowych.
Jak to działa? Kwestia zastosowania wzorów: = B 2 4 A C x 1 = B+ 2 A x 2 = B 2 A
Funkcja delta Powinna liczyć deltę (jak działa). Powinna przyjmować w parametrach współczynniki A,B,C (parametry wejściowe) Powinna zwracać policzoną deltę
Funkcja delta Powinna liczyć deltę (jak działa). Powinna przyjmować w parametrach współczynniki A,B,C (parametry wejściowe) Powinna zwracać policzoną deltę double delta(double A, double B, double C) { double wartoscdelty = B*B - 4*A*C; return delta; }
Funkcja delta nazwa Parametry wejściowe double delta(double A, double B, double C) { double wartoscdelty = B*B - 4*A*C; return delta; } Ciało funkcji Typ wyniku Zwrócenie rezultatu
Funkcja delta po optymalizacji double delta(double A, double B, double C) { return B*B - 4*A*C; }
Funkcja liczx1 Powinna liczyć jeden z pierwiastków. Powinna przyjmować w parametrach współczynniki A,B,C, delta (parametry wejściowe) Powinna zwracać policzony x1
Funkcja liczx1 Powinna liczyć jeden z pierwiastków. Powinna przyjmować w parametrach współczynniki A,B,C, delta (parametry wejściowe) Powinna zwracać policzony x1 double liczx1(double A, double B, double C, double delta) { if(delta>0) return B+sqrt(delta) / 2*A; }
Funkcja liczx1- poprawiamy Zapomnieliśmy o nawiasach! double liczx1(double A, double B, double C, double delta) { if(delta>0) return ( B+sqrt(delta)) / (2*A); }
Funkcja liczx1- poprawiamy Zapomnieliśmy o nawiasach! No i przydałaby się jakaś informacja lub cokolwiek, gdy delta < 0 double liczx1(double A, double B, double C, double delta) { if(delta>0) return ( B+sqrt(delta)) / (2*A); else { cout << Nie da się! << endl; return 0; }}
To jak tego użyć teraz?
Optymalizujemy kod Wartość funkcji można bezpośrednio wypisać na ekran.
Optymalizujemy kod bardziej Wartość jednej funkcji można użyć jako wejście do drugiej funkcji.
Optymalizujemy kod bardziej Wartość jednej funkcji można użyć jako wejście do drugiej funkcji.
Optymalizujemy kod bardziej Czasem zbyt mocna optymalizacja kodu powoduje zwolnienie programu. W tym przykładzie zamiast raz liczyć wartość delty, za każdym razem uruchamiamy funkcją liczącą dokładnie to samo. Więc jest to trochę bez sensu!
Zbyt mocna optymalizacja
Funkcje, które zwracają nic Funkcja zawsze zwraca jakąś wartość, np. double w1 = sin(3.14); Procedura, to taka funkcja, która zwraca nic A nic, to void wyswietl(); Wartość zwracaną przez funkcję można przypisać.
1. Obniżenie kosztu 2. Niezawodność 3. Spójność 4. Mniejsza i łatwiejsza praca dla programistów przy wykorzystaniu gotowego kodu. Ale po co?
Kilka przykładów
Kilka słów o funkcjach/procedurach Procedura to programowe narzędzie realizujące określone czynności. Każda procedura ma swoją nazwę. Wpisanie nazwy procedury w kodzie programu oznacza jej wywołanie. Wywołanie procedury polega na: zawieszeniu wykonania aktualnie realizowanego ciągu instrukcji, wykonaniu instrukcji przypisanych do procedury o danej nazwie, wznowieniu wykonania realizowanego ciągu instrukcji, począwszy od instrukcji następnej po wywołaniu procedury.
Słowniczek pojęć Funkcja/procedura Wywołanie Definicja Deklaracja Argumenty/parametry Rezultat Ciało funkcji
Odp: a)3 ; 4 b)3 ; 3 A co będzie jak napiszemy tak?
Odp: a)3 ; 4 b)3 ; 3 Przy przekazywaniu parametrów przez wartość, wartość parametru aktualnego wywołania funkcji kopiowana jest do parametru formalnego funkcji. Od tego momentu parametr aktualny i formalny są od siebie niezależne.
A jeśli chcemy faktycznie zmienić?
Jeśli chcemy przekazać więcej parametrów niż jeden, np. Po co komu referencja?
Parametry domyślne
Przeciążanie funkcji
Podsumowanie Historycznie i technicznie pierwotna przyczyna wyodrębnienia podprogramów to eliminowanie powtarzających się fragmentów kodu. Z czasem podprogramy stały się podstawowym środkiem podziału programu na mniejsze części, stając się podstawą dla programowania proceduralnego. Stosowanie podprogramów zwykle skraca program zarówno kod źródłowy jak i wynikowy. Program staje się czytelniejszy. Modyfikacje programu stają się łatwiejsze. Łatwiejsze jest lokalizowanie i eliminowanie błędów. Program staje się podatniejszy na modularyzację.