Programovanie (2) v Jave
1-INF-166, letný semester 2023/24

Prednášky · Pravidlá · Softvér · Testovač
· Vyučujúcich predmetu možno kontaktovať mailom na adresách uvedených na hlavnej stránke. Hromadná mailová adresa zo zimného semestra v letnom semestri nefunguje.
· JavaFX: cesta k adresáru lib je v počítačových učebniach /usr/share/openjfx/lib.


Prednáška 5: Rozdiel medzi revíziami

Z Programovanie
Skočit na navigaci Skočit na vyhledávání
 
(44 medziľahlých úprav od 3 ďalších používateľov nie je zobrazených)
Riadok 1: Riadok 1:
 
== Oznamy ==
 
== Oznamy ==
 +
* Dokončenie predchádzajúcej [[Prednáška 4b|prednášky (funkcie)]].
 +
* Na piatkové cvičenia treba prísť, ak ste v utorok na cvičení nezískali aspoň 5 bodov.
 +
** Na testovači máte v záložke Body, položke Piatkove_cvicenie uvedené predbežné body z utorkového cvičenia a napísané, či je alebo nie je pre vás piatkové cvičenie povinné.
 +
** Ak je pre vás cvičenie povinné a neprídete, dostanete -1 bod.
 +
** Môžete prísť aj ak je pre vás cvičenie nepovinné, ale máte nejaké otázky.
 +
* Dnes polia, budúci pondelok hlavne ďalšie príklady a algoritmy s použitím tých častí Cčka, ktoré už poznáte
  
* DÚ1 bude zverejnená pravdepodobne dnes večer
+
==Hľadanie chýb v programe==
* Budúcu stredu večer test
+
* Väčšina programov nefunguje na prvý krát, hľadanie chýb patrí medzi základné činnosti programátora
* Príďte na dnešné 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ň
+
* Podobné chyby sa často opakujú, tréningom sa ich naučíte nájsť rýchlejšie
** 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
 
  
== Opakovanie: spracovanie väčšieho množstva dát ==
+
Ak program ani neskompiluje
 +
* Kompilátor vypíše číslo riadku s chybou, čo vám ju môže pomôcť nájsť
 +
** Občas je však chyba trochu inde, napr. niečo chýba o riadok vyššie
 +
* Pokročilejšie prostredia, ako napr. Netbeans, vám vedia ukázať polohu chyby
 +
* Ak kompilátor vypíše veľa chýb, opravte najskôr prvú, potom skompilujte znovu, ďalšie chyby môžu byť len dôsledkom prvej
 +
* Ak neviete nájsť chybu pri kompilácii, skúste zakomentovať nejaké časti programu pomocou /* */, aby ste zúžili priestor, kde chyba môže byť
 +
* Aj varovania kompilátora môžu poukazovať na chybu v programe
 +
 
 +
Ak program dáva zlé výsledky, "cyklí sa" alebo "padá"
 +
* Môžete skúsiť program znovu prečítať, či nezbadáte chybu
 +
** Pomáha [[Prednáška 3#.C3.9Aprava_a_.C4.8Ditate.C4.BEnos.C5.A5_programov|dobrá čitateľnosť programov]]
 +
* Alebo experimentami zistiť, kde sa jeho správanie prvýkrát začne odlišovať od toho, čo očakávate
 +
* To sa dá robiť spúšťaním programu po krokoch v nástroji nazvanom debugger (nachádza sa napr. v Netbeans)
 +
* Alternatíva k debuggeru je do programu pridať pomocné výpisy, ktoré vám prezradia, ktorá časť programu sa práve vykonáva a aké sú hodnoty dôležitých premenných
 +
** Po nájdení chyby treba tieto pomocné výpisy odstrániť. Pozor, aby ste tým nespravili ďalšiu chybu
 +
* Debugger alebo výpisy vám pomôžu nájsť chybu iba vtedy, ak máte predstavu o tom, ako by program mal fungovať a hľadáte, kde sa od nej skutočné správanie líši
 +
* Pomáha vyrobiť si čo najmenší vstup, kde je zlý výsledok
 +
 
 +
== 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 &xm, double &ym) {
 +
    xm = (x1 + x2) / 2;
 +
    ym = (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 <tt>x</tt> a <tt>y</tt>
 +
* V jednom struct-e môžu byť aj položky rôznych typov, napr.
 +
<syntaxhighlight lang="C++">
 +
struct bod {
 +
  double x,y;
 +
  int id;
 +
  bool visible;
 +
};</syntaxhighlight>
 +
* 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 suradnice bodu v zatvorke a koniec riadku
 +
    cout << "(" << b.x << "," << b.y << ")" << endl;
 +
}
 +
 
 +
int main() {
 +
    // 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 troch bodov). Často však chceme pracovať s väčším množstvom dát
 +
* Dnes 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 (takéto príklady sme už videli na cvičeniach)
  
 
V nasledujúcich príkladoch užívateľ zadá číslo ''N'' a potom ''N'' celých čísel
 
V nasledujúcich príkladoch užívateľ zadá číslo ''N'' a potom ''N'' celých čísel
Riadok 15: Riadok 141:
  
 
=== Priemer ===
 
=== Priemer ===
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     int N;
 
     int N;
 
     cout << "Zadaj pocet cisel: ";
 
     cout << "Zadaj pocet cisel: ";
Riadok 35: Riadok 161:
 
     cout << "Priemer je " << priemer << "." << endl;
 
     cout << "Priemer je " << priemer << "." << endl;
 
}
 
}
</pre>
+
</syntaxhighlight>
 +
 
 +
* Čo by sa stalo, keby sme vo výpočte priemeru vynechali <tt>(double)</tt>?
  
 
=== Maximum ===
 
=== Maximum ===
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     int max, x, N;
 
     int max, x, N;
  
Riadok 61: Riadok 189:
 
     cout << endl << "Maximum je " << max << endl;
 
     cout << endl << "Maximum je " << max << endl;
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
Ako ale začať? Ako nastaviť maximum na začiatok?
 
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?  
+
* Jedna možnosť je nastaviť ho na nejakú veľmi malú hodnotu, aby sa iste neskôr zmenila. Ale čo ak používateľ 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.
+
* Riešením je použiť najmenšie možné číslo. Ale to 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)
+
* Ď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).
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     int max, x, N;
 
     int max, x, N;
  
Riadok 91: Riadok 219:
 
     cout << endl << "Maximum je " << max << endl;
 
     cout << endl << "Maximum je " << max << endl;
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
'''Cvičenie:'''  
 
'''Cvičenie:'''  
 
* Ako by sme program rozšírili tak, aby vedel vypísať aj koľké číslo v poradí bolo najväčšie?
 
* 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?  
+
* Č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 ===
+
== Prvé použitie poľa: podpriemer / nadpriemer ==
  
 
Chceme spočítať priemer a o každom vstupnom čísle vypísať, či je nadpriemerné alebo podpriemerné.
 
Chceme spočítať priemer a o každom vstupnom čísle vypísať, či je nadpriemerné alebo podpriemerné.
Riadok 107: Riadok 234:
 
** pozor, <tt>a[20]</tt> neexistuje
 
** pozor, <tt>a[20]</tt> neexistuje
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     const int N = 20; // premennu N uz nebude mozne menit, ma konstantnu hodnotu 20
 
     const int N = 20; // premennu N uz nebude mozne menit, ma konstantnu hodnotu 20
 
     int a[N];
 
     int a[N];
Riadok 134: Riadok 261:
 
     }
 
     }
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
Na zamyslenie:
 
Na zamyslenie:
Riadok 144: Riadok 271:
  
 
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
<pre>
+
<syntaxhighlight lang="C++">
 
int a[10];
 
int a[10];
 
const int N=20;
 
const int N=20;
 
double b[N];
 
double b[N];
</pre>
+
</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
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     const int NMax = 1000;
 
     const int NMax = 1000;
 
     int p[NMax];
 
     int p[NMax];
Riadok 168: Riadok 295:
 
   cout << "Zadavajte " << N << " cisel: ";
 
   cout << "Zadavajte " << N << " cisel: ";
 
   ...
 
   ...
</pre>
+
</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?
<pre>
+
<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];
</pre>
+
</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 313:
 
V definícii môžeme pole inicializovať zoznamom prvkov.  
 
V definícii môžeme pole inicializovať zoznamom prvkov.  
  
<pre>
+
<syntaxhighlight lang="C++">
int A[4]={3, 6, 8, 10}; //spravne
+
// spravne: inicializacia zoznamom
int B[4]; //spravne
+
int A[4] = {3, 6, 8, 10};  
B[4]={3, 6, 8, 10};  //nespravne, pole tu nedefinujeme
+
// spravne: definicia bez inicializacie
B[0]=3; B[1]=6; B[2]=8; B[3]=10;  // spravne - menime prvky existujuceho pola
+
int B[4];  
</pre>
+
// nespravne, pole tu nedefinujeme:
 +
// B[4] = {3, 6, 8, 10};   
 +
// spravne - menime prvky existujuceho pola:
 +
B[0] = 3; B[1] = 6; B[2] = 8; B[3] = 10;   
 +
</syntaxhighlight>
  
 
=== Indexovanie hodnotou mimo intervalu ===
 
=== Indexovanie hodnotou mimo intervalu ===
  
 
Pozor, kompilátor nekontroluje indexy prvkov
 
Pozor, kompilátor nekontroluje indexy prvkov
<pre>
+
<syntaxhighlight lang="C++">
 
   int a[10];
 
   int a[10];
 
   a[10] = 1234;
 
   a[10] = 1234;
</pre>
+
</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 339:
 
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.
  
<pre>
+
<syntaxhighlight lang="C++">
  for (i=0; i<10; i++) b[i]=a[i];
+
for (int i=0; i < 10; i++) {
</pre>
+
  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 << "rovnake"; }</tt>.
 
síce skompilujete, ale nikdy to nebude pravda – neporovná sa obsah poľa, ale niečo úplne iné (adresy polí v pamäti).
 
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.
 
Treba opäť porovnávať prvok po prvku.
  
<pre>
+
<syntaxhighlight lang="C++">
  bool rovne = true;
+
bool rovne = true;
  for (i = 0; i < 10; i++) {  
+
for (int i = 0; i < 10; i++) {  
      rovne = rovne && a[i] == b[i];  
+
    rovne = rovne && a[i] == b[i];  
  }
+
}
  if (rovne) cout << "Rovnaju sa" << endl;  
+
if (rovne) {
</pre>
+
    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:
  
<pre>
+
<syntaxhighlight lang="C++">
  bool rovne = true;
+
bool rovne = true;
  for (i = 0; i < 10; i++) {  
+
for (int i = 0; i < 10; i++) {  
      if(a[i] != b[i]) {
+
    if(a[i] != b[i]) {
          rovne = false;
+
        rovne = false;
          break;
+
        break;
      }
+
    }
  }
+
}
  if (rovne) cout << "Rovnaju sa" << endl;  
+
if (rovne) {
</pre>
+
    cout << "Rovnaju sa" << endl;  
 +
}
 +
</syntaxhighlight>
  
 
== Výskyty čísel 0...9 ==
 
== 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.  
+
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:
+
Prvý prístup:
 
* vstupné čísla uložíme do poľa
 
* 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''
 
* pre každú hodnotu ''i'' od 0 po 9 prejdeme pole a spočítame počet výskytov ''i''
  
Druhý prístup
+
Druhý prístup:
 
* samotné vstupné čísla neukladáme do poľa, spracovávame ich po jednom
 
* 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''
 
* vytvoríme si pole počítadiel dĺžky 10, v ktorom p[i] bude počet výskytov čísla ''i''
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     int p[10];  // pole dlzky 10
 
     int p[10];  // pole dlzky 10
 
     int N;
 
     int N;
Riadok 276: Riadok 413:
 
         cout << "Pocet vyskytov " << i << " je " << p[i] << endl; // výpis
 
         cout << "Pocet vyskytov " << i << " je " << p[i] << endl; // výpis
 
     }
 
     }
}</pre>
+
}</syntaxhighlight>
 
 
 
 
  
 
== Odbočka: grafická knižnica SVGdraw ==
 
== Odbočka: grafická knižnica SVGdraw ==
Riadok 285: Riadok 420:
 
* 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 [[Zimný semester, softvér#Pr.C3.A1ca_v_Kate_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:
<pre>
+
<syntaxhighlight lang="C++">
 
#include "SVGdraw.h"
 
#include "SVGdraw.h"
  
int main(void) {
+
int main() {
 
   /* Vytvor obrázok s šírkou 150 a výškou 100 a  
 
   /* Vytvor obrázok s šírkou 150 a výškou 100 a  
 
   * ulož ho do súboru stvorec.svg*/
 
   * ulož ho do súboru stvorec.svg*/
Riadok 298: Riadok 433:
 
   /* Nastav farbu vyfarbovania na zelenú. */
 
   /* Nastav farbu vyfarbovania na zelenú. */
 
   drawing.setFillColor("green");
 
   drawing.setFillColor("green");
   /* Vykresli štvorec s ľavým horným rohom v bode (20,10)
+
   /* Vykresli štvorec s ľavým horným  
   * a s dĺžkou strany 80, t.j. pravým dolným rohom 100,90 */
+
  * 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);
 
   drawing.drawRectangle(20,10,80,80);
 
    
 
    
   /* Nastav farbu vyfarbovania na červenú. */
+
   /* Nastav farbu vyfarbovania na červenú */
 
   drawing.setFillColor("red");
 
   drawing.setFillColor("red");
   /* Vykresli kruh so stredom v bode (60,50) a polomerom 40. */
+
   /* Vykresli kruh so stredom  
 +
  * v bode (60,50) a polomerom 40. */
 
   drawing.drawEllipse(60,50,40,40);
 
   drawing.drawEllipse(60,50,40,40);
  
Riadok 310: Riadok 448:
 
   drawing.finish();
 
   drawing.finish();
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
== Kreslíme kruhy ==
 
== Kreslíme kruhy ==
  
 
[[Image:P5-kruhy.png|thumb|150px]]
 
[[Image:P5-kruhy.png|thumb|150px]]
Nasledujúci program náhodne vygeneruje súradnice niekoľkých kruhov a vykreslí ich pomocou kružnice SVGdraw.  
+
Nasledujúci program náhodne vygeneruje súradnice niekoľkých kruhov a vykreslí ich pomocou knižnice SVGdraw.  
 
* Údaje o jednom kruhu si uloží do záznamu <tt>struct kruh</tt>, v programe používame pole takýchto kruhov.
 
* Údaje o jednom kruhu si uloží do záznamu <tt>struct kruh</tt>, 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.
 
* 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.
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include "SVGdraw.h"
 
#include "SVGdraw.h"
 
#include <cstdlib>
 
#include <cstdlib>
Riadok 328: Riadok 466:
 
     int x, y; /* suradnice stredu */
 
     int x, y; /* suradnice stredu */
 
     int polomer; /* polomer kruhu */
 
     int polomer; /* polomer kruhu */
     bool pretinaSa; /* pretína sa s iným kruhom? */
+
     bool pretinaSa; /* pretína sa s iným? */
 
};
 
};
  
 
void generujKruh(kruh &k, int velkost, int polomer) {
 
void generujKruh(kruh &k, int velkost, int polomer) {
     /* inicializuj kruh s nahodnou polohou a danym polomerom */
+
     /* inicializuj kruh s nahodnou polohou  
     k.x = rand() % (velkost - 2 * polomer) + polomer;
+
    * a danym polomerom */
     k.y = rand() % (velkost - 2 * polomer) + polomer;
+
     k.x = rand() % (velkost - 2 * polomer)  
 +
          + polomer;
 +
     k.y = rand() % (velkost - 2 * polomer)  
 +
          + polomer;
 
     k.polomer = polomer;
 
     k.polomer = polomer;
 
}
 
}
Riadok 346: Riadok 487:
 
}
 
}
  
int main(void) {
+
int main() {
 
     const int pocet = 10; /* počet kruhov */
 
     const int pocet = 10; /* počet kruhov */
 
     const int velkost = 300; /* veľkosť obrázku */
 
     const int velkost = 300; /* veľkosť obrázku */
 
     const int polomer = 15; /* polomer kruhu */
 
     const int polomer = 15; /* polomer kruhu */
  
     /* inicializácia generátora pseudonáhodných čísel */
+
     /* inicializácia generátora  
 +
    * pseudonáhodných čísel */
 
     srand(time(NULL));
 
     srand(time(NULL));
  
Riadok 369: Riadok 511:
 
         kruhy[i].pretinaSa = false;
 
         kruhy[i].pretinaSa = false;
 
         for (int j = 0; j < pocet; j++) {
 
         for (int j = 0; j < pocet; j++) {
             if (i != j && pretinajuSa(kruhy[i], kruhy[j])) {
+
             if (i != j && pretinajuSa(kruhy[i],
 +
                                      kruhy[j])) {
 
                 kruhy[i].pretinaSa = true;
 
                 kruhy[i].pretinaSa = true;
 
             }
 
             }
Riadok 383: Riadok 526:
 
             drawing.setLineColor("black");
 
             drawing.setLineColor("black");
 
         }
 
         }
         drawing.drawEllipse(kruhy[i].x, kruhy[i].y,
+
         drawing.drawEllipse(kruhy[i].x,  
                             kruhy[i].polomer, kruhy[i].polomer);
+
                            kruhy[i].y,
 +
                             kruhy[i].polomer,
 +
                            kruhy[i].polomer);
 
     }
 
     }
  
Riadok 390: Riadok 535:
 
     drawing.finish();
 
     drawing.finish();
 
}
 
}
</pre>
+
</syntaxhighlight>
  
== Eratostenovo sito ==
+
== Polia: zhrnutie ==
  
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.
+
* 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ť
* Vytvoríme pole ''A'' pravdivostných hodnôt, kde ''A[i]'' nám hovorí, či je ''i'' ešte potenciálne prvočíslo.  
+
* C resp. C++ nekontrolujú, či index nie je mimo rozsahu poľa
* 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 <tt>true</tt>. Toto číslo je prvočíslo (vypíšeme ho) a vyškrtáme jeho násobky.  
 
 
 
<pre>
 
#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;
 
}
 
</pre>
 
 
 
Výstup programu
 
<pre>
 
2 3 5 7 11 13 17 19 23
 
</pre>
 
 
 
Priebeh programu:
 
<pre>
 
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
 
</pre>
 
 
 
'''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 ==
 
== Ďalšie príklady na prácu s poľom ==
Riadok 446: Riadok 549:
  
 
* Načítajte pole čísel, náhodne ich premiešajte a zase vypíšte.
 
* 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. <tt>int, double, bool</tt>
 
** Bez <tt>&</tt> sa skopíruje hodnota
 
** S <tt>&</tt> premenná dostane vo funkcii nové meno
 
<pre>
 
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);
 
}
 
</pre>
 
* Polia odovzdávame bez <tt>&</tt>
 
** 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
 
<pre>
 
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);
 
}
 
</pre>
 
 
* 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
 
<pre>
 
#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();
 
}
 
</pre>
 
* Štruktúry (<tt>struct</tt>) väčšinou tiež posielame pomocou <tt>&</tt>
 
* Návratové hodnoty:
 
** ak je výsledkom funkcie jedno číslo alebo pravdivostná hodnota, vrátime ju príkazom <tt>return</tt>
 
** ak je výsledkom viac hodnôt, alebo niečo zložitejšie (pole, struct,...), odovzdáme ho ako parameter pomocou <tt>&</tt>, návratová hodnota môže zostať <tt>void</tt>
 
* Tieto pravidlá súvisia so smerníkmi a správou pamäti, povieme si viac o pár týždňov
 

Aktuálna revízia z 18:55, 3. október 2023

Oznamy

  • Dokončenie predchádzajúcej prednášky (funkcie).
  • Na piatkové cvičenia treba prísť, ak ste v utorok na cvičení nezískali aspoň 5 bodov.
    • Na testovači máte v záložke Body, položke Piatkove_cvicenie uvedené predbežné body z utorkového cvičenia a napísané, či je alebo nie je pre vás piatkové cvičenie povinné.
    • Ak je pre vás cvičenie povinné a neprídete, dostanete -1 bod.
    • Môžete prísť aj ak je pre vás cvičenie nepovinné, ale máte nejaké otázky.
  • Dnes polia, budúci pondelok hlavne ďalšie príklady a algoritmy s použitím tých častí Cčka, ktoré už poznáte

Hľadanie chýb v programe

  • Väčšina programov nefunguje na prvý krát, hľadanie chýb patrí medzi základné činnosti programátora
  • Podobné chyby sa často opakujú, tréningom sa ich naučíte nájsť rýchlejšie

Ak program ani neskompiluje

  • Kompilátor vypíše číslo riadku s chybou, čo vám ju môže pomôcť nájsť
    • Občas je však chyba trochu inde, napr. niečo chýba o riadok vyššie
  • Pokročilejšie prostredia, ako napr. Netbeans, vám vedia ukázať polohu chyby
  • Ak kompilátor vypíše veľa chýb, opravte najskôr prvú, potom skompilujte znovu, ďalšie chyby môžu byť len dôsledkom prvej
  • Ak neviete nájsť chybu pri kompilácii, skúste zakomentovať nejaké časti programu pomocou /* */, aby ste zúžili priestor, kde chyba môže byť
  • Aj varovania kompilátora môžu poukazovať na chybu v programe

Ak program dáva zlé výsledky, "cyklí sa" alebo "padá"

  • Môžete skúsiť program znovu prečítať, či nezbadáte chybu
  • Alebo experimentami zistiť, kde sa jeho správanie prvýkrát začne odlišovať od toho, čo očakávate
  • To sa dá robiť spúšťaním programu po krokoch v nástroji nazvanom debugger (nachádza sa napr. v Netbeans)
  • Alternatíva k debuggeru je do programu pridať pomocné výpisy, ktoré vám prezradia, ktorá časť programu sa práve vykonáva a aké sú hodnoty dôležitých premenných
    • Po nájdení chyby treba tieto pomocné výpisy odstrániť. Pozor, aby ste tým nespravili ďalšiu chybu
  • Debugger alebo výpisy vám pomôžu nájsť chybu iba vtedy, ak máte predstavu o tom, ako by program mal fungovať a hľadáte, kde sa od nej skutočné správanie líši
  • Pomáha vyrobiť si čo najmenší vstup, kde je zlý výsledok

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 &xm, double &ym) {
    xm = (x1 + x2) / 2;
    ym = (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 suradnice bodu v zatvorke a koniec riadku
    cout << "(" << b.x << "," << b.y << ")" << endl;
}

int main() {
    // 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 troch bodov). Často však chceme pracovať s väčším množstvom dát

  • Dnes 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 (takéto príklady sme už videli na cvičeniach)

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() {
    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)?

Maximum

#include <iostream>
using namespace std;

int main() {
    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. Ale čo ak používateľ dá všetky čísla ešte menšie?
  • Riešením je použiť najmenšie možné číslo. Ale to 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() {
    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?

Prvé použitie poľa: 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() {
    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() {
    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.

// spravne: inicializacia zoznamom
int A[4] = {3, 6, 8, 10}; 
// spravne: definicia bez inicializacie
int B[4]; 
// nespravne, pole tu nedefinujeme:
// B[4] = {3, 6, 8, 10};  
// spravne - menime prvky existujuceho pola:
B[0] = 3; B[1] = 6; B[2] = 8; B[3] = 10;

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 (int i=0; i < 10; i++) {
   b[i] = a[i];
}

Polia sa tiež nedajú porovnávať pomocou operátora ==. Podmienku if (a==b) { cout << "rovnake"; }. 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 (int 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 (int 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ístup:

  • 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() {
    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

Výsledný obrázok
  • 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() {
  /* 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

P5-kruhy.png

Nasledujúci program náhodne vygeneruje súradnice niekoľkých kruhov a vykreslí ich pomocou kniž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? */
};

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() {
    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();
}

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

Ď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.