Prednáška 4b
Oznamy
- Dnes na cvičeniach papierová rozcvička, vzorové riešenia rozcvičky, možnosť spýtať sa k úlohám, ktoré majú termín do dnes večer.
- Papierová rozcvička sa bude týkať prednášok 1-3.
- Utorok na cvičeniach programátorská rozcvička a ďalšie programátorské úlohy s termínom ďalší pondelok večer.
Opakovanie
Na minulej prednáške sme videli funkcie, ako napríklad túto:
double dlzka(double x1, double y1,
double x2, double y2) {
double dx = x1 - x2;
double dy = y1 - y2;
return sqrt(dx * dx + dy * dy);
}
Všeobecná schéma funkcie
typ_navratovej_hodnoty identifikator_funkcie(zoznam_vstupnych_argumentov) {
telo_funkcie // Môže obsahovať príkazy return
}
Kontrola vstupov
- Nasledujúca funkcia počíta súčet všetkých deliteľov čísla
n. - Funguje správne, ak
nje kladné celé číslo. - Pre záporné
nvráti nulu, čo nie je ideálne.
#include <stdio.h>
int sumOfDivisors(int n) {
int sum = 0;
for (int i = 1; i <= n; i++) {
if (n % i == 0) {
sum += i;
}
}
return sum;
}
int main() {
int n;
printf("Zadaj kladne cele cislo: ");
scanf("%d", &n);
printf("Sucet delitelov %d: %d.\n", n, sumOfDivisors(n));
}
Ako by sme mohli funkciu zlepšiť?
- Preprogramovať ju tak, aby vrátila napr. súčet kladných deliteľov aj pre záporné čísla, čo však pre
n=0? - Vrátiť samotný výsledok ale aj logickú hodnotu, či bol vstup správny, t.j. výsledok je zmyspluplný.
- Momentálne však ešte nevieme z funkcie vrátiť viac ako jednu hodnotu.
- V prípade nesprávneho vstupu do funkcie ukončiť beh programu s nejakou správou o chybe
- Uvidíme nižšie.
- Mnohé jazyky na ošetrovanie podobných chýb používajú mechanizmus výnimiek, uvidíte v letnom semestri.
Použitie funkcie assert
- Na ukončenie celého programu V prípade chyby môžeme použiť
funkciu
assert(treba ` #include `). - Táto funkcia kontroluje platnosť nejakej podmienky, jej vstupom je teda logická hodnota.
- Ak je podmienka splnená, program normálne pokračuje.
- V opačnom prípade sa program zastaví s chybovou hláškou.
Okrem toho, že assert ukončí program, je to aj odkaz pre čitateľa kódu, aké máme na danom mieste programu predpoklady.
#include <stdio.h>
#include <assert.h>
int sumOfDivisors(int n) {
assert(n > 0);
int sum = 0;
for (int i = 1; i <= n; i++) {
if (n % i == 0) {
sum += i;
}
}
return sum;
}
int main() {
int n;
printf("Zadaj kladne cele cislo: ");
scanf("%d", &n);
printf("Sucet delitelov %d: %d.\n", n, sumOfDivisors(n));
}
Príklad behu programu:
Zadaj kladne cele cislo: -10
pokus: pokus.c:5: sumOfDivisors: Assertion `n > 0' failed.
Aborted (core dumped)
Záznam typu struct
V príklade s dĺžkou úsečky sme funkcii posielali veľa parametrov (súradnice x a y):
double dlzka(double x1, double y1,
double x2, double y2) {
double dx = x1 - x2;
double dy = y1 - y2;
return sqrt(dx * dx + dy * dy);
}
Program bude krajší, ak si údaje o jednom bode spojíme do jedného záznamu
typedef struct {
double x, y;
} bod;
- Pomocou
typedef structvytvoríme nový dátový typbod, ktorý má zložkyxay - V jednom
struct-e môžu byť aj položky rôznych typov, napr.
typedef struct {
double x,y;
int id;
bool visible;
} rozsireny_bod;
- Môžeme vytvárať premenné typu
bod, napr.bod a, b; - K položkám bodu pristupujeme pomocou bodky, napr.
a.x = 4.0; - Súradnice bodov môžeme posielať do funkcií ako jeden parameter a tiež môžeme funkcie vracať záznamy typu
struct, ako vidíme v nasledujúcom príklade.
Nasledujúci program načíta súradnice troch bodov, spočíta obvod trojuholníka a stredy všetkých troch strán.
#include <stdio.h>
#include <math.h>
typedef struct {
double x, y; // suradnice bodu v rovine
} bod;
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);
}
bod stred(bod bod1, bod bod2) {
bod stred;
// funkcia vrati bod, ktory je stredom usecky z bodu 1 do bodu 2
stred.x = (bod1.x + bod2.x) / 2;
stred.y = (bod1.y + bod2.y) / 2;
return stred;
}
void vypisBod(bod b) {
// funkcia vypise suradnice bodu v zatvorke a koniec riadku
printf("(%.2lf,%.2lf)\n", b.x, b.y);
}
int main() {
// nacitame suradnice vrcholov trojuholnika
bod A, B, C;
printf("Zadaj suradnice vrcholu A oddelene medzerou: ");
scanf("%lf %lf", &A.x, &A.y);
printf("Zadaj suradnice vrcholu B oddelene medzerou: ");
scanf("%lf %lf", &B.x, &B.y);
printf("Zadaj suradnice vrcholu C oddelene medzerou: ");
scanf("%lf %lf", &C.x, &C.y);
// spocitame dlzky stran
double da = dlzka(B, C);
double db = dlzka(A, C);
double dc = dlzka(A, B);
// vypiseme obvod
printf("Obvod trojuholnika ABC: %.2lf\n", da + db + dc);
// spocitame stredy stran
bod stredAB = stred(A, B);
bod stredAC = stred(A, C);
bod stredBC = stred(B, C);
// vypiseme stredy stran
printf("Stred strany AB: ");
vypisBod(stredAB);
printf("Stred strany AC: ");
vypisBod(stredAC);
printf("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.00
Stred strany AB: (0.00,1.50)
Stred strany AC: (2.00,0.00)
Stred strany BC: (2.00,1.50)
Cvičenie: Napíšte verziu funkcie na súčet deliteľov, ktorá vráti záznam typu struct, ktorý bude obsahovať samotný súčet deliteľov a logickú hodnotu vyjadrujúcu, či je súčet deliteľov platný, t.j. či bolo vstupné číslo kladné. Používa sa takáto funkcia pohodlne v hlavnom programe?
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
- Na ďalšej 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 použí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 <stdio.h>
int main() {
int N;
printf("Zadaj pocet cisel: ");
scanf("%d", &N);
int sucet = 0;
printf("Zadavaj cisla: ");
for (int i = 0; i < N; i++) {
int x;
scanf("%d", &x);
sucet += x;
}
double priemer = sucet / (double) N;
printf("Priemer je %lf.\n", priemer);
}
- Čo by sa stalo, keby sme vo výpočte priemeru vynechali
(double)?
Maximum
#include <stdio.h>
int main() {
int max, x, N;
printf("Zadaj pocet cisel: ");
scanf("%d", &N);
printf("Zadavajte cisla: ");
max = ?
for (int i = 0; i < N; i++) {
scanf("%d", &x);
if (x > max) {
max = x;
}
}
printf("Maximum je %d\n", max);
}
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 <stdio.h>
int main() {
int max, x, N;
printf("Zadaj pocet cisel: ");
scanf("%d", &N);
printf("Zadavajte cisla: ");
scanf("%d", &x); // načítanie prvého čísla
max = x;
for (int i = 1; i < N; i++) { // cyklus cez N-1 ďalších čísel
scanf("%d", &x);
if (x > max) {
max = x;
}
}
printf("Maximum je %d\n", max);
}
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?