Programovanie (1) v C/C++
1-INF-127, ZS 2024/25
Prednáška 5: Rozdiel medzi revíziami
(Vytvorená stránka „== Oznamy == * DÚ1 bude zverejnená pravdepodobne dnes večer * Budúcu stredu večer test * Príďte na doplnkové cvičenia, ak sa vám včera nepodarilo vyriešiť…“) |
|||
Riadok 3: | Riadok 3: | ||
* DÚ1 bude zverejnená pravdepodobne dnes večer | * DÚ1 bude zverejnená pravdepodobne dnes večer | ||
* Budúcu stredu večer test | * Budúcu stredu večer test | ||
− | * Príďte na | + | * Príďte na doplnkové cvičenia, ak sa vám včera nepodarilo vyriešiť veľa príkladov. Pomôžeme vám s ďalšími príkladmi na tento týždeň |
** Aj ak ste nespravili rozcvičku, stále môžete z tohto týždňa dostať 8 bodov z 10 | ** Aj ak ste nespravili rozcvičku, stále môžete z tohto týždňa dostať 8 bodov z 10 | ||
* Skúste si niektoré príklady vyriešiť najskôr na papieri ako tréning na test | * Skúste si niektoré príklady vyriešiť najskôr na papieri ako tréning na test | ||
* Dnes polia (nebudú na teste), budúci pondelok hlavne ďalšie príklady a algoritmy s použitím tých častí Cčka, ktoré už poznáte | * Dnes polia (nebudú na teste), budúci pondelok hlavne ďalšie príklady a algoritmy s použitím tých častí Cčka, ktoré už poznáte | ||
+ | * Typy príkladov: | ||
+ | ** napíšte krátky program alebo funkciu (podobne ako na cvičeniach, ale na papieri) | ||
+ | ** zistite, čo program vypíše pre určitý vstup | ||
+ | ** doplňte chýbajúce časti programu alebo v ňom nájdite chyby | ||
+ | * Písanie programu na papieri môže byť zo začiatku ťažké | ||
+ | ** skúste si niektoré príklady z cvičení napísať najskôr na papier, potom prepísať do počítača | ||
+ | Body z cvičení, domácich úloh a písomiek budete vidieť na testovači | ||
+ | * Momentálne body z cvičení 1 a písomky pre pokročilých | ||
+ | ==Organizačné poznámky k štúdiu== | ||
+ | |||
+ | * Poriadne si skontrolujte, či máte v AIS a v indexe to isté a či sú to tie predmety, ktoré chcete brať. | ||
+ | ** Prípadné problémy riešte na študijnom | ||
+ | * Do štvrtka 11.10. je potrebné si prípadné zmeny v zápise dať potvrdiť na študijnom. Pred koncom tejto doby bývajú na študijnom dlhé rady, choďte čím skôr. Úradné hodiny mimoriadne aj 13:00-15:00. | ||
+ | * Informácia na stránke fakulty: [http://zona.fmph.uniba.sk/zapis/] | ||
+ | |||
+ | == Záznam typu struct == | ||
+ | |||
+ | V príkladoch s obvodom trojuholníka a stredom úsečky sme funkciám posielali veľa parametrov (súradnice x a y) | ||
+ | <syntaxhighlight lang="C++"> | ||
+ | void stred(double x1, double y1, double x2, double y2, double &xs, double &ys) { | ||
+ | xs = (x1 + x2) / 2; | ||
+ | ys = (y1 + y2) / 2; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | Program bude krajší, ak si údaje o jednom bode spojíme do jedného záznamu | ||
+ | <syntaxhighlight lang="C++"> | ||
+ | struct bod { | ||
+ | double x, y; | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | * Pomocou <tt>struct</tt> vytvoríme nový dátový typ <tt>bod</tt>, ktorý má zložky x a y | ||
+ | ** V jednom struct-e môžu byť aj položky rôznych typov (napr. <tt>struct bod { double x,y; int id; bool visible; };</tt>) | ||
+ | * Môžeme vytvárať premenné typu <tt>bod</tt>, napr. <tt>bod a, b;</tt> | ||
+ | * K položkám bodu pristupujeme pomocou bodky, napr. <tt>a.x = 4.0; </tt> | ||
+ | * Do funkcií body posielame radšej referenciou, aby sa zbytočne nekopírovalo veľa hodnôt | ||
+ | |||
+ | Nasledujúci program načíta súradnice troch bodov, spočíta obvod trojuholníka a stredy všetkých troch strán. | ||
+ | <syntaxhighlight lang="C++"> | ||
+ | #include <iostream> | ||
+ | #include <cmath> | ||
+ | using namespace std; | ||
+ | |||
+ | struct bod { | ||
+ | double x, y; // suradnice bodu v rovine | ||
+ | }; | ||
+ | |||
+ | double dlzka(bod &bod1, bod &bod2) { | ||
+ | // funkcia vrati dlzku usecky z bodu 1 do bodu 2 | ||
+ | double dx = bod1.x - bod2.x; | ||
+ | double dy = bod1.y - bod2.y; | ||
+ | return sqrt(dx * dx + dy * dy); | ||
+ | } | ||
+ | |||
+ | void stred(bod &bod1, bod &bod2, bod &stred) { | ||
+ | // funkcia do bodu stred spocita stred usecky z bodu 1 do bodu 2 | ||
+ | stred.x = (bod1.x + bod2.x) / 2; | ||
+ | stred.y = (bod1.y + bod2.y) / 2; | ||
+ | } | ||
+ | |||
+ | void vypisBod(bod &b) { | ||
+ | // funkcia vypise surandice bodu v zatvorke a koniec riadku | ||
+ | cout << "(" << b.x << "," << b.y << ")" << endl; | ||
+ | } | ||
+ | |||
+ | int main(void) { | ||
+ | // nacitame suradnice vrcholov trojuholnika | ||
+ | bod A, B, C; | ||
+ | cout << "Zadaj suradnice vrcholu A oddelene medzerou: "; | ||
+ | cin >> A.x >> A.y; | ||
+ | cout << "Zadaj suradnice vrcholu B oddelene medzerou: "; | ||
+ | cin >> B.x >> B.y; | ||
+ | cout << "Zadaj suradnice vrcholu C oddelene medzerou: "; | ||
+ | cin >> C.x >> C.y; | ||
+ | // spocitame dlzky stran | ||
+ | double da = dlzka(B, C); | ||
+ | double db = dlzka(A, C); | ||
+ | double dc = dlzka(A, B); | ||
+ | // vypiseme obvod | ||
+ | cout << "Obvod trojuholnika ABC: " << da + db + dc << endl; | ||
+ | |||
+ | // spocitame stredy stran | ||
+ | bod stredAB; | ||
+ | stred(A, B, stredAB); | ||
+ | bod stredAC; | ||
+ | stred(A, C, stredAC); | ||
+ | bod stredBC; | ||
+ | stred(B, C, stredBC); | ||
+ | |||
+ | // vypiseme stredy stran | ||
+ | cout << "Stred strany AB: "; | ||
+ | vypisBod(stredAB); | ||
+ | cout << "Stred strany AC: "; | ||
+ | vypisBod(stredAC); | ||
+ | cout << "Stred strany BC: "; | ||
+ | vypisBod(stredBC); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Príklad behu programu: | ||
+ | <pre> | ||
+ | Zadaj suradnice vrcholu A oddelene medzerou: 0 0 | ||
+ | Zadaj suradnice vrcholu B oddelene medzerou: 0 3 | ||
+ | Zadaj suradnice vrcholu C oddelene medzerou: 4 0 | ||
+ | Obvod trojuholnika ABC: 12 | ||
+ | Stred strany AB: (0,1.5) | ||
+ | Stred strany AC: (2,0) | ||
+ | Stred strany BC: (2,1.5) | ||
+ | </pre> | ||
+ | |||
+ | == Spracovanie väčšieho množstva dát == | ||
+ | |||
+ | Naše programy doteraz spracovávali len malý počet vstupných dát načítaných od užívateľa (napr. súradnice trocho bodov). Často však chceme pracovať s väčším množstvom dát | ||
+ | * Na budúcej prednáške si ukážeme, ako uložiť väčšie množstvo dát do poľa | ||
+ | * Na niektoré úlohy však pole nepotrebujeme - údaje môžeme spracovávať rovno ako ich užívateľ zadáva | ||
+ | |||
+ | V nasledujúcich príkladoch užívateľ zadá číslo ''N'' a potom ''N'' celých čísel | ||
+ | * Predstavme si napríklad, že učiteľ zadá body, ktoré študenti dostali na písomke (napr. celé čísla v rozsahu 0..10) | ||
+ | * Z týchto bodov chceme spočítať nejaké štatistiky | ||
+ | |||
+ | === Priemer === | ||
+ | <syntaxhighlight lang="C++"> | ||
+ | #include <iostream> | ||
+ | using namespace std; | ||
+ | |||
+ | int main(void) { | ||
+ | int N; | ||
+ | cout << "Zadaj pocet cisel: "; | ||
+ | cin >> N; | ||
+ | |||
+ | int sucet = 0; | ||
+ | cout << "Zadavaj cisla: "; | ||
+ | for (int i = 0; i < N; i++) { | ||
+ | int x; | ||
+ | cin >> x; | ||
+ | sucet += x; | ||
+ | } | ||
+ | |||
+ | double priemer = sucet / (double) N; | ||
+ | cout << "Priemer je " << priemer << "." << endl; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | * Čo by sa stalo, keby sme vo výpočte priemeru vynechali <tt>(double)</tt>? | ||
== Opakovanie: spracovanie väčšieho množstva dát == | == Opakovanie: spracovanie väčšieho množstva dát == | ||
Riadok 15: | Riadok 160: | ||
=== Priemer === | === Priemer === | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include <iostream> | #include <iostream> | ||
using namespace std; | using namespace std; | ||
Riadok 35: | Riadok 180: | ||
cout << "Priemer je " << priemer << "." << endl; | cout << "Priemer je " << priemer << "." << endl; | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
=== Maximum === | === Maximum === | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include <iostream> | #include <iostream> | ||
using namespace std; | using namespace std; | ||
Riadok 61: | Riadok 206: | ||
cout << endl << "Maximum je " << max << endl; | cout << endl << "Maximum je " << max << endl; | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
Ako ale začať? Ako nastaviť maximum na začiatok? | Ako ale začať? Ako nastaviť maximum na začiatok? | ||
Riadok 68: | Riadok 213: | ||
* Ďalšia možnosť je si pamätať, že ešte nemáme správne nastavené maximum a po načítaní prvého čísla ho nastaviť alebo spracovať prvé číslo zvlášť (mimo cyklu) | * Ďalšia možnosť je si pamätať, že ešte nemáme správne nastavené maximum a po načítaní prvého čísla ho nastaviť alebo spracovať prvé číslo zvlášť (mimo cyklu) | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include <iostream> | #include <iostream> | ||
using namespace std; | using namespace std; | ||
Riadok 91: | Riadok 236: | ||
cout << endl << "Maximum je " << max << endl; | cout << endl << "Maximum je " << max << endl; | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
'''Cvičenie:''' | '''Cvičenie:''' | ||
Riadok 107: | Riadok 252: | ||
** pozor, <tt>a[20]</tt> neexistuje | ** pozor, <tt>a[20]</tt> neexistuje | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include <iostream> | #include <iostream> | ||
using namespace std; | using namespace std; | ||
Riadok 134: | Riadok 279: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
Na zamyslenie: | Na zamyslenie: | ||
Riadok 144: | Riadok 289: | ||
Rozsah poľa je konštantný výraz väčší ako 0. Prvky sa indexujú od 0 po počet - 1 | Rozsah poľa je konštantný výraz väčší ako 0. Prvky sa indexujú od 0 po počet - 1 | ||
− | < | + | <syntaxhighlight lang="C++"> |
int a[10]; | int a[10]; | ||
const int N=20; | const int N=20; | ||
double b[N]; | double b[N]; | ||
− | </ | + | </syntaxhighlight> |
Ak nepoznáme vopred počet prvkov, ktoré chceme dať do poľa, môžeme odhadnúť, že ich nebude viac ako NMax, ktoré definujeme ako konštantu v programe. | Ak nepoznáme vopred počet prvkov, ktoré chceme dať do poľa, môžeme odhadnúť, že ich nebude viac ako NMax, ktoré definujeme ako konštantu v programe. | ||
* Vytvoríme pole veľkosti NMax, použijeme z neho len prých N hodnôt | * Vytvoríme pole veľkosti NMax, použijeme z neho len prých N hodnôt | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include <iostream> | #include <iostream> | ||
using namespace std; | using namespace std; | ||
Riadok 168: | Riadok 313: | ||
cout << "Zadavajte " << N << " cisel: "; | cout << "Zadavajte " << N << " cisel: "; | ||
... | ... | ||
− | </ | + | </syntaxhighlight> |
Čo ak ako veľkosť poľa použijeme premennú N, ktorú si prečítame od používateľa alebo inak spočítame za behu? | Čo ak ako veľkosť poľa použijeme premennú N, ktorú si prečítame od používateľa alebo inak spočítame za behu? | ||
− | < | + | <syntaxhighlight lang="C++"> |
int N; | int N; | ||
cout << "Zadaj pocet cisel: "; | cout << "Zadaj pocet cisel: "; | ||
cin >> N; | cin >> N; | ||
int p[N]; | int p[N]; | ||
− | </ | + | </syntaxhighlight> |
* V starších verziách C resp. C++ to nefunguje, aj keď niektoré novšie kompilátory to zvládajú | * V starších verziách C resp. C++ to nefunguje, aj keď niektoré novšie kompilátory to zvládajú | ||
* Na prednáškach tento spôsob nebudeme používať | * Na prednáškach tento spôsob nebudeme používať | ||
Riadok 186: | Riadok 331: | ||
V definícii môžeme pole inicializovať zoznamom prvkov. | V definícii môžeme pole inicializovať zoznamom prvkov. | ||
− | < | + | <syntaxhighlight lang="C++"> |
int A[4]={3, 6, 8, 10}; //spravne | int A[4]={3, 6, 8, 10}; //spravne | ||
int B[4]; //spravne | int B[4]; //spravne | ||
B[4]={3, 6, 8, 10}; //nespravne, pole tu nedefinujeme | B[4]={3, 6, 8, 10}; //nespravne, pole tu nedefinujeme | ||
B[0]=3; B[1]=6; B[2]=8; B[3]=10; // spravne - menime prvky existujuceho pola | B[0]=3; B[1]=6; B[2]=8; B[3]=10; // spravne - menime prvky existujuceho pola | ||
− | </ | + | </syntaxhighlight> |
=== Indexovanie hodnotou mimo intervalu === | === Indexovanie hodnotou mimo intervalu === | ||
Pozor, kompilátor nekontroluje indexy prvkov | Pozor, kompilátor nekontroluje indexy prvkov | ||
− | < | + | <syntaxhighlight lang="C++"> |
int a[10]; | int a[10]; | ||
a[10] = 1234; | a[10] = 1234; | ||
− | </ | + | </syntaxhighlight> |
* Skompiluje, ale hodnota 1234 sa zapíše do pamäti na zlé miesto | * Skompiluje, ale hodnota 1234 sa zapíše do pamäti na zlé miesto | ||
* Môže to mať nepredvídateľné následky: prepísanie obsahu iných premenných (chybný výpočet alebo „nevysvetliteľné“ správanie sa programu) alebo dokonca prepísanie časti kódu vášho programu | * Môže to mať nepredvídateľné následky: prepísanie obsahu iných premenných (chybný výpočet alebo „nevysvetliteľné“ správanie sa programu) alebo dokonca prepísanie časti kódu vášho programu | ||
Riadok 208: | Riadok 353: | ||
Takýto príkaz však neskompiluje – nedá sa takto priraďovať, treba kopírovať prvok po prvku. | Takýto príkaz však neskompiluje – nedá sa takto priraďovať, treba kopírovať prvok po prvku. | ||
− | < | + | <syntaxhighlight lang="C++"> |
for (i=0; i<10; i++) b[i]=a[i]; | for (i=0; i<10; i++) b[i]=a[i]; | ||
− | </ | + | </syntaxhighlight> |
Polia sa tiež nedajú porovnávať pomocou operátora <tt>==</tt>. Podmienku <tt>if (a==b) cout << "Ok";</tt>. | Polia sa tiež nedajú porovnávať pomocou operátora <tt>==</tt>. Podmienku <tt>if (a==b) cout << "Ok";</tt>. | ||
Riadok 216: | Riadok 361: | ||
Treba opäť porovnávať prvok po prvku. | Treba opäť porovnávať prvok po prvku. | ||
− | < | + | <syntaxhighlight lang="C++"> |
bool rovne = true; | bool rovne = true; | ||
for (i = 0; i < 10; i++) { | for (i = 0; i < 10; i++) { | ||
Riadok 222: | Riadok 367: | ||
} | } | ||
if (rovne) cout << "Rovnaju sa" << endl; | if (rovne) cout << "Rovnaju sa" << endl; | ||
− | </ | + | </syntaxhighlight> |
Ten istý kúsok programu môžeme napísať napr. aj takto: | Ten istý kúsok programu môžeme napísať napr. aj takto: | ||
− | < | + | <syntaxhighlight lang="C++"> |
bool rovne = true; | bool rovne = true; | ||
for (i = 0; i < 10; i++) { | for (i = 0; i < 10; i++) { | ||
Riadok 235: | Riadok 380: | ||
} | } | ||
if (rovne) cout << "Rovnaju sa" << endl; | if (rovne) cout << "Rovnaju sa" << endl; | ||
− | </ | + | </syntaxhighlight> |
== Výskyty čísel 0...9 == | == Výskyty čísel 0...9 == | ||
Riadok 249: | Riadok 394: | ||
* vytvoríme si pole počítadiel dĺžky 10, v ktorom p[i] bude počet výskytov čísla ''i'' | * vytvoríme si pole počítadiel dĺžky 10, v ktorom p[i] bude počet výskytov čísla ''i'' | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include <iostream> | #include <iostream> | ||
using namespace std; | using namespace std; | ||
Riadok 276: | Riadok 421: | ||
cout << "Pocet vyskytov " << i << " je " << p[i] << endl; // výpis | cout << "Pocet vyskytov " << i << " je " << p[i] << endl; // výpis | ||
} | } | ||
− | }</ | + | }</syntaxhighlight> |
Riadok 284: | Riadok 429: | ||
[[Image:P5-svgdraw.png|frame|Výsledný obrázok]] | [[Image:P5-svgdraw.png|frame|Výsledný obrázok]] | ||
* Aplikácie s grafickým rozhraním budeme programovať až budúci semester | * Aplikácie s grafickým rozhraním budeme programovať až budúci semester | ||
− | * V tomto semestri, ale budeme kresliť obrázky v [https://en.wikipedia.org/wiki/Scalable_Vector_Graphics SVG formáte] pomocou jednoduchej knižnice [[SVGdraw]] | + | * V tomto semestri, ale budeme kresliť obrázky v [https://en.wikipedia.org/wiki/Scalable_Vector_Graphics SVG formáte] pomocou jednoduchej knižnice [[#SVGdraw]] |
− | * Knižnicu si stiahnite a nainštalujte [[Netbeans#Pr.C3.A1ca_v_Netbeans_s_grafickou_kni.C5.BEnicou_SVGdraw|podľa návodu]] | + | * Knižnicu si stiahnite a nainštalujte [[#Netbeans#Pr.C3.A1ca_v_Netbeans_s_grafickou_kni.C5.BEnicou_SVGdraw|podľa návodu]] |
* Na začiatku programu zapnite knižnicu pomocou <tt>#include "SVGdraw.h"</tt> | * Na začiatku programu zapnite knižnicu pomocou <tt>#include "SVGdraw.h"</tt> | ||
* Tu je malý ukážkový program, ktorý vykreslí zelený štvorec a v ňom červený kruh: | * Tu je malý ukážkový program, ktorý vykreslí zelený štvorec a v ňom červený kruh: | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include "SVGdraw.h" | #include "SVGdraw.h" | ||
Riadok 310: | Riadok 455: | ||
drawing.finish(); | drawing.finish(); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
== Kreslíme kruhy == | == Kreslíme kruhy == | ||
Riadok 319: | Riadok 464: | ||
* Navyše o každom kruhu zistí, či sa pretína s iným kruhom, a ak áno, pri vykresľovaní ho orámuje červenou farbou. | * Navyše o každom kruhu zistí, či sa pretína s iným kruhom, a ak áno, pri vykresľovaní ho orámuje červenou farbou. | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include "SVGdraw.h" | #include "SVGdraw.h" | ||
#include <cstdlib> | #include <cstdlib> | ||
Riadok 390: | Riadok 535: | ||
drawing.finish(); | drawing.finish(); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
== Eratostenovo sito == | == Eratostenovo sito == | ||
Riadok 401: | Riadok 546: | ||
* Potom prechádzame v poli, kým nenájdeme najbližšiu ďalšiu hodnotu <tt>true</tt>. Toto číslo je prvočíslo (vypíšeme ho) a vyškrtáme jeho násobky. | * Potom prechádzame v poli, kým nenájdeme najbližšiu ďalšiu hodnotu <tt>true</tt>. Toto číslo je prvočíslo (vypíšeme ho) a vyškrtáme jeho násobky. | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include <iostream> | #include <iostream> | ||
using namespace std; | using namespace std; | ||
Riadok 422: | Riadok 567: | ||
cout << endl; | cout << endl; | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
Výstup programu | Výstup programu | ||
Riadok 457: | Riadok 602: | ||
** Bez <tt>&</tt> sa skopíruje hodnota | ** Bez <tt>&</tt> sa skopíruje hodnota | ||
** S <tt>&</tt> premenná dostane vo funkcii nové meno | ** S <tt>&</tt> premenná dostane vo funkcii nové meno | ||
− | < | + | <syntaxhighlight lang="C++"> |
void f1(int x) { | void f1(int x) { | ||
x++; // zmena x sa neprenesie do main (y zostane rovnaká) | x++; // zmena x sa neprenesie do main (y zostane rovnaká) | ||
Riadok 475: | Riadok 620: | ||
//zle: f2(y+1); | //zle: f2(y+1); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
* Polia odovzdávame bez <tt>&</tt> | * Polia odovzdávame bez <tt>&</tt> | ||
** väčšinou potrebujeme poslať aj veľkosť poľa, ak nie je globálne známa | ** väčšinou potrebujeme poslať aj veľkosť poľa, ak nie je globálne známa | ||
** zmeny v poli zostanú aj po skončení funkcie | ** zmeny v poli zostanú aj po skončení funkcie | ||
− | < | + | <syntaxhighlight lang="C++"> |
void f(int a[], int n) { | void f(int a[], int n) { | ||
for (int i = 0; i < n; i++) { | for (int i = 0; i < n; i++) { | ||
Riadok 490: | Riadok 635: | ||
f(b, 3); | f(b, 3); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
* SVGdraw obrázky sú v skutočnosti objekty, väčšinou ich chcete posielať s <tt>&</tt> | * SVGdraw obrázky sú v skutočnosti objekty, väčšinou ich chcete posielať s <tt>&</tt> | ||
** všetky zmeny na nich spravené pretrvávajú aj po skončení funkcie | ** všetky zmeny na nich spravené pretrvávajú aj po skončení funkcie | ||
− | < | + | <syntaxhighlight lang="C++"> |
#include "SVGdraw.h" | #include "SVGdraw.h" | ||
void kresli(SVGdraw &drawing, int n, int y, int rozostup, int velkost) { | void kresli(SVGdraw &drawing, int n, int y, int rozostup, int velkost) { | ||
Riadok 506: | Riadok 651: | ||
drawing.finish(); | drawing.finish(); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
* Štruktúry (<tt>struct</tt>) väčšinou tiež posielame pomocou <tt>&</tt> | * Štruktúry (<tt>struct</tt>) väčšinou tiež posielame pomocou <tt>&</tt> | ||
* Návratové hodnoty: | * Návratové hodnoty: |
Verzia zo dňa a času 11:57, 8. október 2019
Obsah
- 1 Oznamy
- 2 Organizačné poznámky k štúdiu
- 3 Záznam typu struct
- 4 Spracovanie väčšieho množstva dát
- 5 Opakovanie: spracovanie väčšieho množstva dát
- 6 Polia
- 7 Výskyty čísel 0...9
- 8 Odbočka: grafická knižnica SVGdraw
- 9 Kreslíme kruhy
- 10 Eratostenovo sito
- 11 Ďalšie príklady na prácu s poľom
- 12 Polia: zhrnutie
- 13 Parametre funkcií - prehľad, opakovanie
Oznamy
- DÚ1 bude zverejnená pravdepodobne dnes večer
- Budúcu stredu večer test
- Príďte na doplnkové cvičenia, ak sa vám včera nepodarilo vyriešiť veľa príkladov. Pomôžeme vám s ďalšími príkladmi na tento týždeň
- Aj ak ste nespravili rozcvičku, stále môžete z tohto týždňa dostať 8 bodov z 10
- Skúste si niektoré príklady vyriešiť najskôr na papieri ako tréning na test
- Dnes polia (nebudú na teste), budúci pondelok hlavne ďalšie príklady a algoritmy s použitím tých častí Cčka, ktoré už poznáte
- Typy príkladov:
- napíšte krátky program alebo funkciu (podobne ako na cvičeniach, ale na papieri)
- zistite, čo program vypíše pre určitý vstup
- doplňte chýbajúce časti programu alebo v ňom nájdite chyby
- Písanie programu na papieri môže byť zo začiatku ťažké
- skúste si niektoré príklady z cvičení napísať najskôr na papier, potom prepísať do počítača
Body z cvičení, domácich úloh a písomiek budete vidieť na testovači
- Momentálne body z cvičení 1 a písomky pre pokročilých
Organizačné poznámky k štúdiu
- Poriadne si skontrolujte, či máte v AIS a v indexe to isté a či sú to tie predmety, ktoré chcete brať.
- Prípadné problémy riešte na študijnom
- Do štvrtka 11.10. je potrebné si prípadné zmeny v zápise dať potvrdiť na študijnom. Pred koncom tejto doby bývajú na študijnom dlhé rady, choďte čím skôr. Úradné hodiny mimoriadne aj 13:00-15:00.
- Informácia na stránke fakulty: [1]
Záznam typu struct
V príkladoch s obvodom trojuholníka a stredom úsečky sme funkciám posielali veľa parametrov (súradnice x a y)
void stred(double x1, double y1, double x2, double y2, double &xs, double &ys) {
xs = (x1 + x2) / 2;
ys = (y1 + y2) / 2;
}
Program bude krajší, ak si údaje o jednom bode spojíme do jedného záznamu
struct bod {
double x, y;
};
- Pomocou struct vytvoríme nový dátový typ bod, ktorý má zložky x a y
- V jednom struct-e môžu byť aj položky rôznych typov (napr. struct bod { double x,y; int id; bool visible; };)
- Môžeme vytvárať premenné typu bod, napr. bod a, b;
- K položkám bodu pristupujeme pomocou bodky, napr. a.x = 4.0;
- Do funkcií body posielame radšej referenciou, aby sa zbytočne nekopírovalo veľa hodnôt
Nasledujúci program načíta súradnice troch bodov, spočíta obvod trojuholníka a stredy všetkých troch strán.
#include <iostream>
#include <cmath>
using namespace std;
struct bod {
double x, y; // suradnice bodu v rovine
};
double dlzka(bod &bod1, bod &bod2) {
// funkcia vrati dlzku usecky z bodu 1 do bodu 2
double dx = bod1.x - bod2.x;
double dy = bod1.y - bod2.y;
return sqrt(dx * dx + dy * dy);
}
void stred(bod &bod1, bod &bod2, bod &stred) {
// funkcia do bodu stred spocita stred usecky z bodu 1 do bodu 2
stred.x = (bod1.x + bod2.x) / 2;
stred.y = (bod1.y + bod2.y) / 2;
}
void vypisBod(bod &b) {
// funkcia vypise surandice bodu v zatvorke a koniec riadku
cout << "(" << b.x << "," << b.y << ")" << endl;
}
int main(void) {
// nacitame suradnice vrcholov trojuholnika
bod A, B, C;
cout << "Zadaj suradnice vrcholu A oddelene medzerou: ";
cin >> A.x >> A.y;
cout << "Zadaj suradnice vrcholu B oddelene medzerou: ";
cin >> B.x >> B.y;
cout << "Zadaj suradnice vrcholu C oddelene medzerou: ";
cin >> C.x >> C.y;
// spocitame dlzky stran
double da = dlzka(B, C);
double db = dlzka(A, C);
double dc = dlzka(A, B);
// vypiseme obvod
cout << "Obvod trojuholnika ABC: " << da + db + dc << endl;
// spocitame stredy stran
bod stredAB;
stred(A, B, stredAB);
bod stredAC;
stred(A, C, stredAC);
bod stredBC;
stred(B, C, stredBC);
// vypiseme stredy stran
cout << "Stred strany AB: ";
vypisBod(stredAB);
cout << "Stred strany AC: ";
vypisBod(stredAC);
cout << "Stred strany BC: ";
vypisBod(stredBC);
}
Príklad behu programu:
Zadaj suradnice vrcholu A oddelene medzerou: 0 0 Zadaj suradnice vrcholu B oddelene medzerou: 0 3 Zadaj suradnice vrcholu C oddelene medzerou: 4 0 Obvod trojuholnika ABC: 12 Stred strany AB: (0,1.5) Stred strany AC: (2,0) Stred strany BC: (2,1.5)
Spracovanie väčšieho množstva dát
Naše programy doteraz spracovávali len malý počet vstupných dát načítaných od užívateľa (napr. súradnice trocho bodov). Často však chceme pracovať s väčším množstvom dát
- Na budúcej prednáške si ukážeme, ako uložiť väčšie množstvo dát do poľa
- Na niektoré úlohy však pole nepotrebujeme - údaje môžeme spracovávať rovno ako ich užívateľ zadáva
V nasledujúcich príkladoch užívateľ zadá číslo N a potom N celých čísel
- Predstavme si napríklad, že učiteľ zadá body, ktoré študenti dostali na písomke (napr. celé čísla v rozsahu 0..10)
- Z týchto bodov chceme spočítať nejaké štatistiky
Priemer
#include <iostream>
using namespace std;
int main(void) {
int N;
cout << "Zadaj pocet cisel: ";
cin >> N;
int sucet = 0;
cout << "Zadavaj cisla: ";
for (int i = 0; i < N; i++) {
int x;
cin >> x;
sucet += x;
}
double priemer = sucet / (double) N;
cout << "Priemer je " << priemer << "." << endl;
}
- Čo by sa stalo, keby sme vo výpočte priemeru vynechali (double)?
Opakovanie: spracovanie väčšieho množstva dát
V nasledujúcich príkladoch užívateľ zadá číslo N a potom N celých čísel
- Predstavme si napríklad, že učiteľ zadá body, ktoré študenti dostali na písomke (napr. celé čísla v rozsahu 0..10)
- Z týchto bodov chceme spočítať nejaké štatistiky
Priemer
#include <iostream>
using namespace std;
int main(void) {
int N;
cout << "Zadaj pocet cisel: ";
cin >> N;
int sucet = 0;
cout << "Zadavaj cisla: ";
for (int i = 0; i < N; i++) {
int x;
cin >> x;
sucet += x;
}
double priemer = sucet / (double) N;
cout << "Priemer je " << priemer << "." << endl;
}
Maximum
#include <iostream>
using namespace std;
int main(void) {
int max, x, N;
cout << "Zadaj pocet cisel: ";
cin >> N;
cout << "Zadavajte cisla: ";
max = ?
for (int i = 0; i < N; i++) {
cin >> x;
if (x > max) {
max = x;
}
}
cout << endl << "Maximum je " << max << endl;
}
Ako ale začať? Ako nastaviť maximum na začiatok?
- Jedna možnosť je nastaviť ho na nejakú veľmi malú hodnotu, aby sa iste neskôr zmenila. Kto nám ale zaručí, že používateľ nedá všetky čísla ešte menšie?
- Riešením je použiť najmenšie možné číslo - ale je príliš viazané na konkrétny rozsah, nebude fungovať po zmene typu premenných.
- Ďalšia možnosť je si pamätať, že ešte nemáme správne nastavené maximum a po načítaní prvého čísla ho nastaviť alebo spracovať prvé číslo zvlášť (mimo cyklu)
#include <iostream>
using namespace std;
int main(void) {
int max, x, N;
cout << "Zadaj pocet cisel: ";
cin >> N;
cout << "Zadavajte cisla: ";
cin >> x; // načítanie prvého čísla
max = x;
for (int i = 1; i < N; i++) { // cyklus cez N-1 ďalších čísel
cin >> x;
if (x > max) {
max = x;
}
}
cout << endl << "Maximum je " << max << endl;
}
Cvičenie:
- Ako by sme program rozšírili tak, aby vedel vypísať aj koľké číslo v poradí bolo najväčšie?
- Čo treba v programe zmeniť, ak chceme hľadať minimum namiesto maxima?
- Spočítajte, koľkokrát sa na vstupe vyskytuje číslo 0
Podpriemer / nadpriemer
Chceme spočítať priemer a o každom vstupnom čísle vypísať, či je nadpriemerné alebo podpriemerné.
- Priemer vieme až keď načítame všetky čísla, musíme si ich teda zapamätať
- Na to používame tabuľky, ktoré sa volajú polia.
- Na začiatok pre jednoduchosť predpokladajme, že vopred vieme, že počet údajov N je napr. 20 (N teda nenačítavame)
- Príkaz int a[20]; vytvorí pole s 20 premennými typu int, ku ktorým pristupujeme a[0], a[1], ..., a[19]
- pozor, a[20] neexistuje
#include <iostream>
using namespace std;
int main(void) {
const int N = 20; // premennu N uz nebude mozne menit, ma konstantnu hodnotu 20
int a[N];
double sucet = 0;
cout << "Zadavajte " << N << " cisel: ";
for (int i = 0; i < N; i++) {
cin >> a[i];
sucet += a[i];
}
double priemer = sucet / N;
cout << "Priemer je " << priemer << "." << endl;
for (int i = 0; i < N; i++) {
if (a[i] > priemer) {
cout << a[i] << ": vacsie ako priemer." << endl;
} else if (a[i] < priemer) {
cout << a[i] << ": mensie ako priemer." << endl;
} else {
cout << a[i] << ": priemer." << endl;
}
}
}
Na zamyslenie:
- Pozor, chyby pri zaokrúhľovaní môžu spôsobiť, že niekedy priemerné číslo bude považované za nad/podpriemerné
- Vedeli by ste program prerobiť tak, aby používal iba premenné typu int a nerobil žiadnu chybu v zaokrúhľovaní?
- Môže aj po takejto zmene niekedy dať zlú odpoveď?
Polia
Rozsah poľa je konštantný výraz väčší ako 0. Prvky sa indexujú od 0 po počet - 1
int a[10];
const int N=20;
double b[N];
Ak nepoznáme vopred počet prvkov, ktoré chceme dať do poľa, môžeme odhadnúť, že ich nebude viac ako NMax, ktoré definujeme ako konštantu v programe.
- Vytvoríme pole veľkosti NMax, použijeme z neho len prých N hodnôt
#include <iostream>
using namespace std;
int main(void) {
const int NMax = 1000;
int p[NMax];
int N;
cout << "Zadaj pocet cisel: ";
cin >> N;
if (N > NMax) {
cout << "Prilis velke N" << endl;
return 1; // ukončíme funkciu main a tým aj program
}
cout << "Zadavajte " << N << " cisel: ";
...
Čo ak ako veľkosť poľa použijeme premennú N, ktorú si prečítame od používateľa alebo inak spočítame za behu?
int N;
cout << "Zadaj pocet cisel: ";
cin >> N;
int p[N];
- V starších verziách C resp. C++ to nefunguje, aj keď niektoré novšie kompilátory to zvládajú
- Na prednáškach tento spôsob nebudeme používať
- Pole veľkosti N, ktorá nie je konštanta, sa naučíme vytvárať inak v druhej polovici semestra
Vytvorenie a inicializácia poľa
V definícii môžeme pole inicializovať zoznamom prvkov.
int A[4]={3, 6, 8, 10}; //spravne
int B[4]; //spravne
B[4]={3, 6, 8, 10}; //nespravne, pole tu nedefinujeme
B[0]=3; B[1]=6; B[2]=8; B[3]=10; // spravne - menime prvky existujuceho pola
Indexovanie hodnotou mimo intervalu
Pozor, kompilátor nekontroluje indexy prvkov
int a[10];
a[10] = 1234;
- Skompiluje, ale hodnota 1234 sa zapíše do pamäti na zlé miesto
- Môže to mať nepredvídateľné následky: prepísanie obsahu iných premenných (chybný výpočet alebo „nevysvetliteľné“ správanie sa programu) alebo dokonca prepísanie časti kódu vášho programu
Kopírovanie a testovanie rovnosti
V prípade, že chceme vytvoriť pole, ktoré je kópiou už existujúceho poľa, ponúka sa možnosť príkazu priradenia b=a;. Takýto príkaz však neskompiluje – nedá sa takto priraďovať, treba kopírovať prvok po prvku.
for (i=0; i<10; i++) b[i]=a[i];
Polia sa tiež nedajú porovnávať pomocou operátora ==. Podmienku if (a==b) cout << "Ok";. síce skompilujete, ale nikdy to nebude pravda – neporovná sa obsah poľa, ale niečo úplne iné (adresy polí v pamäti). Treba opäť porovnávať prvok po prvku.
bool rovne = true;
for (i = 0; i < 10; i++) {
rovne = rovne && a[i] == b[i];
}
if (rovne) cout << "Rovnaju sa" << endl;
Ten istý kúsok programu môžeme napísať napr. aj takto:
bool rovne = true;
for (i = 0; i < 10; i++) {
if(a[i] != b[i]) {
rovne = false;
break;
}
}
if (rovne) cout << "Rovnaju sa" << endl;
Výskyty čísel 0...9
Na vstupe je číslo N' a N celých čísel od 0 do 9 a chceme vedieť, koľkokrát sa jednotlivé čísla na vstupe vyskytli.
Prvý prístupy:
- vstupné čísla uložíme do poľa
- pre každú hodnotu i od 0 po 9 prejdeme pole a spočítame počet výskytov i
Druhý prístup
- samotné vstupné čísla neukladáme do poľa, spracovávame ich po jednom
- vytvoríme si pole počítadiel dĺžky 10, v ktorom p[i] bude počet výskytov čísla i
#include <iostream>
using namespace std;
int main(void) {
int p[10]; // pole dlzky 10
int N;
cout << "Zadajte pocet cisel: ";
cin >> N;
for (int i = 0; i < 10; i++) {
p[i] = 0; // inicializácia poľa p[0]=0; p[1]=0; ... p[9]=0;
}
cout << "Zadavajte " << N << "cisel z intervalu 0-9: ";
for (int i = 0; i < N; i++) {
int x;
cin >> x;
if (x >= 0 && x < 10) { // test, či je číslo z požadovaného rozsahu
p[x]++; // zvýšime počítadlo pre hodnotu x
}
}
cout << endl;
for (int i = 0; i < 10; i++) {
cout << "Pocet vyskytov " << i << " je " << p[i] << endl; // výpis
}
}
Odbočka: grafická knižnica SVGdraw
- Aplikácie s grafickým rozhraním budeme programovať až budúci semester
- V tomto semestri, ale budeme kresliť obrázky v SVG formáte pomocou jednoduchej knižnice #SVGdraw
- Knižnicu si stiahnite a nainštalujte podľa návodu
- Na začiatku programu zapnite knižnicu pomocou #include "SVGdraw.h"
- Tu je malý ukážkový program, ktorý vykreslí zelený štvorec a v ňom červený kruh:
#include "SVGdraw.h"
int main(void) {
/* Vytvor obrázok s šírkou 150 a výškou 100 a
* ulož ho do súboru stvorec.svg*/
SVGdraw drawing(150, 100, "stvorec.svg");
/* Nastav farbu vyfarbovania na zelenú. */
drawing.setFillColor("green");
/* Vykresli štvorec s ľavým horným rohom v bode (20,10)
* a s dĺžkou strany 80, t.j. pravým dolným rohom 100,90 */
drawing.drawRectangle(20,10,80,80);
/* Nastav farbu vyfarbovania na červenú. */
drawing.setFillColor("red");
/* Vykresli kruh so stredom v bode (60,50) a polomerom 40. */
drawing.drawEllipse(60,50,40,40);
/* Ukonči vypisovanie obrázka. */
drawing.finish();
}
Kreslíme kruhy
Nasledujúci program náhodne vygeneruje súradnice niekoľkých kruhov a vykreslí ich pomocou kružnice SVGdraw.
- Údaje o jednom kruhu si uloží do záznamu struct kruh, v programe používame pole takýchto kruhov.
- Navyše o každom kruhu zistí, či sa pretína s iným kruhom, a ak áno, pri vykresľovaní ho orámuje červenou farbou.
#include "SVGdraw.h"
#include <cstdlib>
#include <ctime>
#include <cmath>
struct kruh {
int x, y; /* suradnice stredu */
int polomer; /* polomer kruhu */
bool pretinaSa; /* pretína sa s iným kruhom? */
};
void generujKruh(kruh &k, int velkost, int polomer) {
/* inicializuj kruh s nahodnou polohou a danym polomerom */
k.x = rand() % (velkost - 2 * polomer) + polomer;
k.y = rand() % (velkost - 2 * polomer) + polomer;
k.polomer = polomer;
}
bool pretinajuSa(kruh &k1, kruh &k2) {
/* zisti, ci sa dva kruhy pretinaju */
int dx = k1.x - k2.x;
int dy = k1.y - k2.y;
double d2 = sqrt(dx * dx + dy * dy);
return d2 <= k1.polomer + k2.polomer;
}
int main(void) {
const int pocet = 10; /* počet kruhov */
const int velkost = 300; /* veľkosť obrázku */
const int polomer = 15; /* polomer kruhu */
/* inicializácia generátora pseudonáhodných čísel */
srand(time(NULL));
/* inicializácia obrázku */
SVGdraw drawing(velkost, velkost, "kruhy.svg");
/* pole kruhov */
kruh kruhy[pocet];
for (int i = 0; i < pocet; i++) {
/* kazdemu kruhu vygeneruj nahodnu polohu */
generujKruh(kruhy[i], velkost, polomer);
}
/* zisti, ktoré kruhy pretínajú iné kruhy */
for (int i = 0; i < pocet; i++) {
kruhy[i].pretinaSa = false;
for (int j = 0; j < pocet; j++) {
if (i != j && pretinajuSa(kruhy[i], kruhy[j])) {
kruhy[i].pretinaSa = true;
}
}
}
/* vykresluj kruhy */
drawing.setFillColor("gray");
for (int i = 0; i < pocet; i++) {
if (kruhy[i].pretinaSa) {
drawing.setLineColor("red");
} else {
drawing.setLineColor("black");
}
drawing.drawEllipse(kruhy[i].x, kruhy[i].y,
kruhy[i].polomer, kruhy[i].polomer);
}
/* ukoncenie vykreslovania */
drawing.finish();
}
Eratostenovo sito
Chceme vypísať všetky prvočísla medzi 2 a N. Mohli by sme ísť cez všetky čísla a pre každé testovať, koľko má deliteľov (deliteľov sme už hľadali predtým), ale vieme to spraviť aj rýchlejšie. Použijeme algoritmus zvaný Eratostenovo sito.
- Vytvoríme pole A pravdivostných hodnôt, kde A[i] nám hovorí, či je i ešte potenciálne prvočíslo.
- Na začiatku budú všetky hodnoty true, lebo sme ešte žiadne číslo nevylúčili.
- Začneme číslom 2 - toto je iste prvočíslo (tak ho vypíšeme). O jeho násobkoch však vieme, že iste nemôžu byť prvočísla - nastavíme preto pre každý násobok j=2*k pravdivostnú hodnotu A[j] na false.
- Potom prechádzame v poli, kým nenájdeme najbližšiu ďalšiu hodnotu true. Toto číslo je prvočíslo (vypíšeme ho) a vyškrtáme jeho násobky.
#include <iostream>
using namespace std;
int main(void) {
const int N = 25;
bool A[N + 1];
for (int i = 2; i <= N; i++) {
A[i] = true;
}
for (int i = 2; i <= N; i++) {
if (A[i]) {
cout << i << " ";
for (int j = 2 * i; j <= N; j = j + i) {
A[j] = false;
}
}
}
cout << endl;
}
Výstup programu
2 3 5 7 11 13 17 19 23
Priebeh programu:
0 1 2 3 4 5 6 7 8 9 10 11 12 ... ? ? T T T T T T T T T T T ... na zaciatku ? ? T T F T F T F T F T F ... po vyskrtani i=2 ? ? T T F T F T F F F T F ... po vyskrtani i=3 ? ? T T F T F T F F F T F ... dalej sa uz skrtaju len vacsie cisla
Cvičenie: Napíšte funkciu, ktorá uloží prvočísla medzi 2 a N do poľa (ak by sme ich chceli použiť na ďalšie výpočty).
Ďalšie príklady na prácu s poľom
- Načítajte pole čísel a vypíšte ho v opačnom poradí.
- Skúste poradie povymieňať priamo v poli a nie iba pri výpise.
- Načítajte pole čísel, náhodne ich premiešajte a zase vypíšte.
Polia: zhrnutie
- Pole je tabuľka hodnôt. V poli dĺžky n máme hodnoty p[0], p[1], ..., p[n-1]
- Kopírovanie a porovnávanie polí si musíme naprogramovať
- C resp. C++ nekontrolujú, či index nie je mimo rozsahu poľa
Parametre funkcií - prehľad, opakovanie
- Jednoduché typy, napr. int, double, bool
- Bez & sa skopíruje hodnota
- S & premenná dostane vo funkcii nové meno
void f1(int x) {
x++; // zmena x sa neprenesie do main (y zostane rovnaká)
cout << x << endl;
}
void f2(int &x) {
x++; // zmena x sa prenesie ako zmena y v main
cout << x << endl;
}
int main(void) {
int y = 0;
f1(y);
f2(y);
f1(y + 1);
//zle: f2(y+1);
}
- Polia odovzdávame bez &
- väčšinou potrebujeme poslať aj veľkosť poľa, ak nie je globálne známa
- zmeny v poli zostanú aj po skončení funkcie
void f(int a[], int n) {
for (int i = 0; i < n; i++) {
cout << a[i] << endl;
}
}
int main(void) {
int b[3] = {1, 2, 3};
f(b, 3);
}
- SVGdraw obrázky sú v skutočnosti objekty, väčšinou ich chcete posielať s &
- všetky zmeny na nich spravené pretrvávajú aj po skončení funkcie
#include "SVGdraw.h"
void kresli(SVGdraw &drawing, int n, int y, int rozostup, int velkost) {
for(int i=0; i<n; i++) {
drawing.drawRectangle(i*rozostup+velkost, y, velkost, velkost);
}
}
int main(void) {
SVGdraw drawing(320, 100, "stvorce.svg");
kresli(drawing, 10, 50, 30, 10);
drawing.finish();
}
- Štruktúry (struct) väčšinou tiež posielame pomocou &
- Návratové hodnoty:
- ak je výsledkom funkcie jedno číslo alebo pravdivostná hodnota, vrátime ju príkazom return
- ak je výsledkom viac hodnôt, alebo niečo zložitejšie (pole, struct,...), odovzdáme ho ako parameter pomocou &, návratová hodnota môže zostať void
- Tieto pravidlá súvisia so smerníkmi a správou pamäti, povieme si viac o pár týždňov