1120 lines
28 KiB
Markdown
1120 lines
28 KiB
Markdown
# Rust — Iterátory, Closures, Kombinácie (D–G)
|
||
|
||
Pre každú tému: **vysvetlenie** → **príklady** → **úlohy na precvičenie** → riešenia na konci.
|
||
|
||
---
|
||
|
||
## Časť D: Iterátory
|
||
|
||
### D.1 — Čo je iterátor
|
||
|
||
Iterátor je spôsob, ako prejsť cez kolekciu prvok po prvku. V Ruste existujú 3 spôsoby ako získať iterátor z kolekcie:
|
||
|
||
| Metóda | Dáva ti | Kedy použiť |
|
||
|--------|---------|-------------|
|
||
| `.iter()` | `&T` (nemeniteľná referencia) | Chceš len čítať |
|
||
| `.iter_mut()` | `&mut T` (meniteľná referencia) | Chceš meniť prvky na mieste |
|
||
| `.into_iter()` | `T` (vlastníctvo) | Chceš konzumovať kolekciu |
|
||
|
||
```rust
|
||
let mut cisla = vec![1, 2, 3];
|
||
|
||
// iter() — len čítam, cisla existujú ďalej
|
||
for c in cisla.iter() {
|
||
println!("{}", c); // c je &i32
|
||
}
|
||
|
||
// iter_mut() — mením prvky
|
||
for c in cisla.iter_mut() {
|
||
*c += 10; // c je &mut i32, treba dereferencovať
|
||
}
|
||
// cisla je teraz [11, 12, 13]
|
||
|
||
// into_iter() — preberám vlastníctvo, cisla po tomto neexistujú
|
||
let cisla2 = vec![1, 2, 3];
|
||
for c in cisla2.into_iter() {
|
||
println!("{}", c); // c je i32
|
||
}
|
||
// cisla2 tu už nemôžeš použiť!
|
||
```
|
||
|
||
**Pravidlo:** Ak máš `&self` metódu (len čítaš), použi `.iter()`. Ak máš `&mut self`, použi `.iter_mut()`.
|
||
|
||
---
|
||
|
||
### D.2 — filter
|
||
|
||
**Čo robí:** Prejde cez iterátor a nechá len prvky, pre ktoré closure vráti `true`.
|
||
|
||
**Typ:** `.filter()` dáva referencie na referencie. Ak iteruješ cez `&T`, vo filtri dostaneš `&&T`.
|
||
|
||
```rust
|
||
let cisla = vec![1, 2, 3, 4, 5, 6];
|
||
|
||
// Nechaj len párne
|
||
let parne: Vec<&i32> = cisla.iter().filter(|c| *c % 2 == 0).collect();
|
||
// parne = [&2, &4, &6]
|
||
|
||
// Ak chceš Vec<i32> (nie referencie), použi .copied() alebo .cloned()
|
||
let parne: Vec<i32> = cisla.iter().filter(|c| *c % 2 == 0).copied().collect();
|
||
// parne = [2, 4, 6]
|
||
```
|
||
|
||
**So štruktúrami:**
|
||
```rust
|
||
struct Kniha { nazov: String, rok: u16 }
|
||
|
||
let knihy = vec![
|
||
Kniha { nazov: "A".into(), rok: 2020 },
|
||
Kniha { nazov: "B".into(), rok: 2015 },
|
||
];
|
||
|
||
// Knihy po roku 2018
|
||
let nove: Vec<&Kniha> = knihy.iter().filter(|k| k.rok > 2018).collect();
|
||
```
|
||
|
||
**Na skúške:** Každá metóda typu `daj_knihy_podla_X` je `filter` + `collect`.
|
||
|
||
---
|
||
|
||
### D.3 — find
|
||
|
||
**Čo robí:** Vráti **prvý** prvok, pre ktorý closure vráti `true`. Vráti `Option`.
|
||
|
||
```rust
|
||
let cisla = vec![1, 2, 3, 4, 5];
|
||
|
||
let prve_parne: Option<&i32> = cisla.iter().find(|c| *c % 2 == 0);
|
||
// Some(&2)
|
||
|
||
let viac_ako_10: Option<&i32> = cisla.iter().find(|c| **c > 10);
|
||
// None
|
||
```
|
||
|
||
**So štruktúrami:**
|
||
```rust
|
||
let knihy = vec![
|
||
Kniha { nazov: "Hobbit".into(), rok: 1937 },
|
||
Kniha { nazov: "Duna".into(), rok: 1965 },
|
||
];
|
||
|
||
// Nájdi knihu podľa názvu
|
||
let najdena: Option<&Kniha> = knihy.iter().find(|k| k.nazov == "Duna");
|
||
```
|
||
|
||
**Na skúške:** Každá metóda typu `daj_knihu_podla_isbn` je `find`.
|
||
|
||
---
|
||
|
||
### D.4 — position
|
||
|
||
**Čo robí:** Ako `find`, ale vráti **index** namiesto hodnoty. Vráti `Option<usize>`.
|
||
|
||
```rust
|
||
let mena = vec!["Anna".to_string(), "Boris".to_string(), "Cyril".to_string()];
|
||
|
||
let index: Option<usize> = mena.iter().position(|m| m == "Boris");
|
||
// Some(1)
|
||
```
|
||
|
||
**Hlavné použitie — odstránenie z Vec:**
|
||
```rust
|
||
fn odstran(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()> {
|
||
// 1. nájdi index
|
||
let pos = zoznam.iter().position(|s| s == meno).ok_or(())?;
|
||
// 2. odstráň na tom indexe (remove posunie zvyšné prvky)
|
||
Ok(zoznam.remove(pos))
|
||
}
|
||
```
|
||
|
||
**Na skúške:** Každá metóda `odstran_knihu`, `odstran_kontakt` atď. je `position` + `remove`.
|
||
|
||
---
|
||
|
||
### D.5 — map
|
||
|
||
**Čo robí:** Transformuje každý prvok. `[A, B, C]` → `[f(A), f(B), f(C)]`.
|
||
|
||
```rust
|
||
let cisla = vec![1, 2, 3];
|
||
|
||
// Zdvojnásob každé
|
||
let dvojnasobok: Vec<i32> = cisla.iter().map(|c| c * 2).collect();
|
||
// [2, 4, 6]
|
||
|
||
// Prevod na String
|
||
let texty: Vec<String> = cisla.iter().map(|c| c.to_string()).collect();
|
||
// ["1", "2", "3"]
|
||
|
||
// Vytiahni pole zo štruktúry
|
||
let nazvy: Vec<&String> = knihy.iter().map(|k| &k.nazov).collect();
|
||
```
|
||
|
||
---
|
||
|
||
### D.6 — any a all
|
||
|
||
**Čo robia:** Vracajú `bool`.
|
||
|
||
```rust
|
||
let cisla = vec![1, -2, 3, -4, 5];
|
||
|
||
// any — existuje aspoň jeden, pre ktorý platí?
|
||
let ma_zaporne: bool = cisla.iter().any(|c| *c < 0);
|
||
// true
|
||
|
||
// all — platí pre VŠETKY?
|
||
let vsetky_kladne: bool = cisla.iter().all(|c| *c > 0);
|
||
// false
|
||
```
|
||
|
||
---
|
||
|
||
### D.7 — sum a count
|
||
|
||
```rust
|
||
let cisla = vec![1, 2, 3, 4, 5];
|
||
|
||
// Súčet — musíš špecifikovať typ
|
||
let sucet: i32 = cisla.iter().sum();
|
||
// alebo
|
||
let sucet = cisla.iter().sum::<i32>();
|
||
// 15
|
||
|
||
// Počet prvkov spĺňajúcich podmienku
|
||
let pocet_parnych: usize = cisla.iter().filter(|c| *c % 2 == 0).count();
|
||
// 2
|
||
```
|
||
|
||
---
|
||
|
||
### D.8 — max_by_key a min_by_key
|
||
|
||
**Čo robí:** Nájde maximum/minimum podľa kľúča. Vráti `Option`.
|
||
|
||
```rust
|
||
struct Zamestnanec { meno: String, plat: u32 }
|
||
|
||
let zamestnanci = vec![
|
||
Zamestnanec { meno: "Anna".into(), plat: 3000 },
|
||
Zamestnanec { meno: "Boris".into(), plat: 4500 },
|
||
Zamestnanec { meno: "Cyril".into(), plat: 2800 },
|
||
];
|
||
|
||
let najbohatsi: Option<&Zamestnanec> = zamestnanci.iter().max_by_key(|z| z.plat);
|
||
// Some(Boris)
|
||
```
|
||
|
||
---
|
||
|
||
### D.9 — enumerate
|
||
|
||
**Čo robí:** Pridá index ku každému prvku. `[A, B, C]` → `[(0, A), (1, B), (2, C)]`.
|
||
|
||
```rust
|
||
let mena = vec!["Anna", "Boris", "Cyril"];
|
||
|
||
for (i, meno) in mena.iter().enumerate() {
|
||
println!("{}. {}", i + 1, meno);
|
||
}
|
||
// 1. Anna
|
||
// 2. Boris
|
||
// 3. Cyril
|
||
```
|
||
|
||
---
|
||
|
||
### D.10 — Reťazenie
|
||
|
||
Iterátory sa dajú reťaziť. Každá metóda vráti nový iterátor:
|
||
|
||
```rust
|
||
let zamestnanci = vec![/* ... */];
|
||
|
||
// Mená zamestnancov s platom nad 3000, zoradené
|
||
let bohati_mena: Vec<&String> = zamestnanci.iter()
|
||
.filter(|z| z.plat > 3000) // nechaj len bohatých
|
||
.map(|z| &z.meno) // vytiahni meno
|
||
.collect(); // zhromaždi do Vec
|
||
```
|
||
|
||
**Kedy for, kedy iterátor:**
|
||
- Jednoduchý prechod s výpisom → `for`
|
||
- Filtrovanie → `.filter().collect()`
|
||
- Transformácia → `.map().collect()`
|
||
- Hľadanie → `.find()` alebo `.position()`
|
||
- Podmienka → `.any()` alebo `.all()`
|
||
- Počítanie → `.filter().count()`
|
||
- Ak robíš kombináciu hore → reťaz iterátorov
|
||
|
||
---
|
||
|
||
### Úlohy D
|
||
|
||
**D-1.** Máš `Vec<i32>` = `[3, -1, 4, -1, 5, 9, -2, 6]`. Napíš jedným výrazom (bez for):
|
||
- a) Počet kladných čísel
|
||
- b) Súčet záporných čísel
|
||
- c) Vec len kladných čísel (ako `Vec<i32>`, nie referencie)
|
||
- d) Či sú všetky čísla väčšie ako -5
|
||
- e) Či existuje číslo väčšie ako 8
|
||
|
||
**D-2.** Máš:
|
||
```rust
|
||
struct Student { meno: String, vek: u8, priemer: f32 }
|
||
let studenti: Vec<Student> = /* ... */;
|
||
```
|
||
Napíš (každé na jeden riadok):
|
||
- a) Mená študentov s priemerom pod 2.0 → `Vec<&String>`
|
||
- b) Najstarší študent → `Option<&Student>`
|
||
- c) Priemerný vek všetkých študentov → `Option<f64>`
|
||
- d) Existuje študent mladší ako 18? → `bool`
|
||
- e) Odstráň študenta podľa mena z `&mut Vec<Student>` → `Result<Student, ()>`
|
||
|
||
**D-3.** Prepíš na iterátorový zápis bez `for` a bez `mut` premenných:
|
||
```rust
|
||
fn kladne_zdvojnasobene(cisla: &[i32]) -> Vec<i32> {
|
||
let mut vysledok = Vec::new();
|
||
for c in cisla {
|
||
if *c > 0 {
|
||
vysledok.push(c * 2);
|
||
}
|
||
}
|
||
vysledok
|
||
}
|
||
```
|
||
|
||
**D-4.** Čo je zle? Oprav:
|
||
```rust
|
||
fn zvys_vsetky(cisla: &mut Vec<i32>) {
|
||
for c in cisla.iter() {
|
||
*c += 1;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Časť E: Closures (uzávery)
|
||
|
||
### E.1 — Čo je closure
|
||
|
||
Closure je anonymná funkcia, ktorá môže zachytiť premenné z okolitého prostredia.
|
||
|
||
```rust
|
||
// Normálna funkcia — nemá prístup k okoliu
|
||
fn pricti_5(x: i32) -> i32 {
|
||
x + 5
|
||
}
|
||
|
||
// Closure — zachytí premennú `zaklad` z okolia
|
||
let zaklad = 5;
|
||
let pricti = |x: i32| -> i32 { x + zaklad };
|
||
|
||
println!("{}", pricti(10)); // 15
|
||
```
|
||
|
||
### E.2 — Syntax closure
|
||
|
||
Od najdlhšieho po najkratšie:
|
||
|
||
```rust
|
||
// Plný zápis (ako funkcia, ale s ||)
|
||
let f = |x: i32, y: i32| -> i32 { x + y };
|
||
|
||
// Bez typov (Rust si ich odvodí)
|
||
let f = |x, y| { x + y };
|
||
|
||
// Bez zátvoriek (ak je len jeden výraz)
|
||
let f = |x, y| x + y;
|
||
|
||
// Bez parametrov
|
||
let f = || println!("ahoj");
|
||
|
||
// S jedným parametrom
|
||
let f = |x| x * 2;
|
||
```
|
||
|
||
**Pravidlo:** V `.filter()`, `.map()`, `.find()` atď. vždy píš najkratší zápis.
|
||
|
||
### E.3 — Closures v iterátoroch
|
||
|
||
Toto sú najčastejšie situácie:
|
||
|
||
```rust
|
||
let cisla = vec![1, 2, 3, 4, 5];
|
||
|
||
// filter — closure berie &&T (referenciu na referenciu)
|
||
cisla.iter().filter(|c| **c > 3) // c je &&i32, treba **
|
||
cisla.iter().filter(|&c| *c > 3) // destructuring v parametri
|
||
cisla.iter().filter(|&&c| c > 3) // plný destructuring
|
||
|
||
// map — closure berie &T
|
||
cisla.iter().map(|c| c * 2) // c je &i32, Rust automaticky dereferencuje pri *
|
||
|
||
// find — rovnako ako filter
|
||
cisla.iter().find(|c| **c == 3)
|
||
|
||
// any/all — rovnako ako filter
|
||
cisla.iter().any(|c| *c > 3) // tu je len &T, nie &&T!
|
||
```
|
||
|
||
**Prečo filter má `&&T`?** Lebo `.iter()` dáva `&T`, a filter ešte obalí ďalšou referenciou. Ale Rust je v praxi dosť chytrý — väčšinou stačí `|c| c > &3` alebo `|c| *c > 3`.
|
||
|
||
### E.4 — move closure
|
||
|
||
Normálne closure zachytáva referencie. `move` prenesie vlastníctvo do closure:
|
||
|
||
```rust
|
||
// BEZ move — closure zachytí &meno
|
||
let meno = "Anna".to_string();
|
||
let pozdrav = || println!("Ahoj {}", meno);
|
||
pozdrav();
|
||
println!("{}", meno); // OK, meno stále existuje
|
||
|
||
// S move — closure prevezme vlastníctvo mena
|
||
let meno = "Anna".to_string();
|
||
let pozdrav = move || println!("Ahoj {}", meno);
|
||
pozdrav();
|
||
// println!("{}", meno); // CHYBA! meno bolo presunuté do closure
|
||
```
|
||
|
||
**Kedy treba move:** Keď closure musí prežiť pôvodnú premennú (napr. vrátenie closure z funkcie).
|
||
|
||
```rust
|
||
fn vytvor_pozdrav(meno: String) -> impl Fn() {
|
||
// BEZ move: CHYBA — meno zomrie na konci funkcie, ale closure ho referencuje
|
||
// S move: OK — closure vlastní meno
|
||
move || println!("Ahoj {}", meno)
|
||
}
|
||
```
|
||
|
||
### E.5 — Closure zachytávajúci &mut
|
||
|
||
Ak closure modifikuje premennú, zachytí ju ako `&mut`:
|
||
|
||
```rust
|
||
let mut pocitadlo = 0;
|
||
let mut zvys = || { pocitadlo += 1; };
|
||
zvys();
|
||
zvys();
|
||
zvys();
|
||
println!("{}", pocitadlo); // 3
|
||
```
|
||
|
||
**Dôležité:** Kým existuje `&mut` closure, nemôžeš čítať premennú inak:
|
||
```rust
|
||
let mut cisla = vec![1, 2, 3];
|
||
let mut pridaj = |x| cisla.push(x);
|
||
pridaj(4);
|
||
// println!("{:?}", cisla); // CHYBA TU! pridaj stále drží &mut cisla
|
||
pridaj(5);
|
||
drop(pridaj); // explicitne uvoľni closure
|
||
println!("{:?}", cisla); // OK teraz — [1, 2, 3, 4, 5]
|
||
```
|
||
|
||
Alebo jednoducho — posledné použitie closure pred prvým iným použitím:
|
||
```rust
|
||
let mut cisla = vec![1, 2, 3];
|
||
let mut pridaj = |x| cisla.push(x);
|
||
pridaj(4);
|
||
pridaj(5); // posledné volanie
|
||
println!("{:?}", cisla); // OK — Rust vie, že pridaj sa už nepoužije
|
||
```
|
||
|
||
---
|
||
|
||
### Úlohy E
|
||
|
||
**E-1.** Napíš 3 verzie tej istej operácie (vyfiltruj čísla > 10 z Vec<i32>):
|
||
- a) S pomenovanou funkciou (nie closure)
|
||
- b) S plným zápisom closure (aj typy)
|
||
- c) S najkratším zápisom
|
||
|
||
**E-2.** Čo je zle? Oprav:
|
||
```rust
|
||
fn vrat_nasobicku(faktor: i32) -> impl Fn(i32) -> i32 {
|
||
|x| x * faktor
|
||
}
|
||
```
|
||
|
||
**E-3.** Napíš funkciu `aplikuj_na_vsetky(cisla: &[i32], f: impl Fn(i32) -> i32) -> Vec<i32>` ktorá aplikuje funkciu `f` na každý prvok.
|
||
Potom ju zavolaj s:
|
||
- a) closure, ktorý zdvojnásobí
|
||
- b) closure, ktorý pripočíta 100
|
||
- c) pomenovanou funkciou, ktorá vráti absolútnu hodnotu
|
||
|
||
**E-4.** Máš `Vec<String>`. Napíš closure, ktorý:
|
||
- Zachytí `&mut Vec<String>`
|
||
- Pridá reťazec do vektora
|
||
- Zavolaj ho 3x, potom vypíš výsledok
|
||
|
||
---
|
||
|
||
## Časť F: HashMap
|
||
|
||
### F.1 — Základy
|
||
|
||
```rust
|
||
use std::collections::HashMap;
|
||
|
||
let mut mapa: HashMap<String, i32> = HashMap::new();
|
||
|
||
// Vloženie
|
||
mapa.insert("Anna".to_string(), 25);
|
||
mapa.insert("Boris".to_string(), 30);
|
||
|
||
// Čítanie — vráti Option<&V>
|
||
let vek: Option<&i32> = mapa.get("Anna"); // Some(&25)
|
||
let vek: Option<&i32> = mapa.get("Cyril"); // None
|
||
|
||
// Kontrola existencie
|
||
let existuje: bool = mapa.contains_key("Anna"); // true
|
||
|
||
// Iterácia
|
||
for (meno, vek) in &mapa {
|
||
println!("{}: {}", meno, vek);
|
||
}
|
||
```
|
||
|
||
### F.2 — Entry API (dôležité na skúške!)
|
||
|
||
`entry()` je spôsob, ako pracovať s kľúčom, ktorý môže alebo nemusí existovať:
|
||
|
||
```rust
|
||
use std::collections::HashMap;
|
||
|
||
let mut pocty: HashMap<String, usize> = HashMap::new();
|
||
let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"];
|
||
|
||
// Počítaj výskyty
|
||
for slovo in &slova {
|
||
// entry() vráti Entry — buď Occupied alebo Vacant
|
||
// or_insert(0) — ak kľúč neexistuje, vlož 0
|
||
// vráti &mut hodnotu
|
||
*pocty.entry(slovo.to_string()).or_insert(0) += 1;
|
||
}
|
||
// pocty = {"ahoj": 3, "svet": 2, "rust": 1}
|
||
```
|
||
|
||
**Rozklad:**
|
||
```rust
|
||
*pocty.entry(slovo.to_string()).or_insert(0) += 1;
|
||
// ^^^^^ nájdi alebo vytvor kľúč
|
||
// ^^^^^^^^^^^^ ak neexistuje, vlož 0
|
||
// ^^^^ vráti &mut usize
|
||
// ^^^ += 1 dereferencuj a zvýš
|
||
```
|
||
|
||
**Na skúške:** Metódy ako `vypis_vydavatelstva_a_pocet_knih` alebo `pocet_v_kategoriach` vždy používajú tento vzor.
|
||
|
||
### F.3 — HashMap zo štruktúr
|
||
|
||
Typický vzor na skúške — zoskup podľa poľa a spočítaj:
|
||
|
||
```rust
|
||
struct Kniha { nazov: String, vydavatelstvo: String }
|
||
|
||
fn pocty_podla_vydavatelstva(knihy: &[Kniha]) -> HashMap<String, usize> {
|
||
let mut mapa = HashMap::new();
|
||
for kniha in knihy {
|
||
*mapa.entry(kniha.vydavatelstvo.clone()).or_insert(0) += 1;
|
||
}
|
||
mapa
|
||
}
|
||
|
||
// Výpis:
|
||
fn vypis_statistiky(knihy: &[Kniha]) {
|
||
let pocty = pocty_podla_vydavatelstva(knihy);
|
||
for (vydavatelstvo, pocet) in &pocty {
|
||
println!("{}: {}", vydavatelstvo, pocet);
|
||
}
|
||
}
|
||
```
|
||
|
||
### F.4 — or_insert_with a or_default
|
||
|
||
```rust
|
||
// or_insert_with — lazy verzia, closure sa zavolá len ak kľúč neexistuje
|
||
mapa.entry(kluc).or_insert_with(|| Vec::new());
|
||
|
||
// or_default — použije Default trait
|
||
// Pre Vec → prázdny vektor, pre usize → 0, pre String → ""
|
||
mapa.entry(kluc).or_default();
|
||
|
||
// Príklad: zoskupenie kníh podľa žánru
|
||
let mut podla_zanru: HashMap<String, Vec<&Kniha>> = HashMap::new();
|
||
for kniha in &knihy {
|
||
podla_zanru.entry(kniha.zaner.clone()).or_default().push(kniha);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Úlohy F
|
||
|
||
**F-1.** Napíš funkciu `pocitaj_znaky(text: &str) -> HashMap<char, usize>` — počet výskytov každého znaku.
|
||
|
||
**F-2.** Máš `Vec<Student>` kde Student má `meno`, `trieda: String`, `priemer: f32`. Napíš:
|
||
- a) `pocet_v_triedach(studenti: &[Student]) -> HashMap<String, usize>` — koľko študentov v každej triede
|
||
- b) `najlepsi_v_triede(studenti: &[Student]) -> HashMap<String, &Student>` — študent s najnižším priemerom v každej triede
|
||
|
||
**F-3.** Napíš funkciu `invertuj_mapu(mapa: &HashMap<String, String>) -> HashMap<String, String>` — otočí kľúče a hodnoty.
|
||
|
||
---
|
||
|
||
## Časť G: Option/Result kombinátory
|
||
|
||
### G.1 — .map() na Option
|
||
|
||
**Čo robí:** Ak je `Some(x)`, aplikuje funkciu na `x`. Ak `None`, zostane `None`.
|
||
|
||
```rust
|
||
let a: Option<i32> = Some(5);
|
||
let b: Option<i32> = None;
|
||
|
||
let a2: Option<i32> = a.map(|x| x * 2); // Some(10)
|
||
let b2: Option<i32> = b.map(|x| x * 2); // None
|
||
|
||
// Praktické — vytiahni pole zo Option<&Struct>
|
||
let kniha: Option<&Kniha> = najdi_knihu("Duna");
|
||
let nazov: Option<&String> = kniha.map(|k| &k.nazov);
|
||
|
||
// Prevod typu
|
||
let cislo: Option<i32> = Some(42);
|
||
let text: Option<String> = cislo.map(|c| c.to_string()); // Some("42")
|
||
```
|
||
|
||
**Nahrádza:**
|
||
```rust
|
||
// Toto:
|
||
match opt {
|
||
Some(x) => Some(f(x)),
|
||
None => None,
|
||
}
|
||
// Je to isté ako:
|
||
opt.map(f)
|
||
```
|
||
|
||
### G.2 — .and_then() (flatmap)
|
||
|
||
**Čo robí:** Ako `.map()`, ale closure vracia `Option`. Zabraňuje `Option<Option<T>>`.
|
||
|
||
```rust
|
||
// map by vytvorilo Option<Option<i32>>:
|
||
let text: Option<String> = Some("42".to_string());
|
||
let zle: Option<Option<i32>> = text.map(|t| t.parse::<i32>().ok());
|
||
|
||
// and_then "sploští":
|
||
let text: Option<String> = Some("42".to_string());
|
||
let dobre: Option<i32> = text.and_then(|t| t.parse::<i32>().ok());
|
||
// Some(42)
|
||
|
||
let text2: Option<String> = Some("abc".to_string());
|
||
let dobre2: Option<i32> = text2.and_then(|t| t.parse::<i32>().ok());
|
||
// None (parse zlyhalo)
|
||
```
|
||
|
||
**Pravidlo:** Ak tvoja closure vracia `Option` → použi `.and_then()`. Ak vracia holú hodnotu → použi `.map()`.
|
||
|
||
### G.3 — .map_err()
|
||
|
||
**Čo robí:** Transformuje chybu v `Result`, hodnotu necháva.
|
||
|
||
```rust
|
||
// read_to_string vracia Result<String, io::Error>
|
||
// Ty chceš Result<String, String>
|
||
fn nacitaj(cesta: &str) -> Result<String, String> {
|
||
std::fs::read_to_string(cesta)
|
||
.map_err(|e| format!("Chyba čítania: {}", e))
|
||
}
|
||
```
|
||
|
||
### G.4 — .flatten()
|
||
|
||
**Čo robí:** `Option<Option<T>>` → `Option<T>`, `Result<Result<T, E>, E>` → `Result<T, E>`.
|
||
|
||
```rust
|
||
let a: Option<Option<i32>> = Some(Some(5));
|
||
let b: Option<Option<i32>> = Some(None);
|
||
let c: Option<Option<i32>> = None;
|
||
|
||
a.flatten() // Some(5)
|
||
b.flatten() // None
|
||
c.flatten() // None
|
||
```
|
||
|
||
### G.5 — .unwrap_or(), .unwrap_or_default(), .unwrap_or_else()
|
||
|
||
```rust
|
||
let a: Option<i32> = None;
|
||
|
||
a.unwrap_or(0) // 0 — vždy vyhodnotí fallback
|
||
a.unwrap_or_default() // 0 — použije Default trait (i32 default = 0)
|
||
a.unwrap_or_else(|| { // lazy — closure sa zavolá len ak None
|
||
println!("Počítam fallback...");
|
||
vypocitaj_nieco()
|
||
})
|
||
```
|
||
|
||
**Kedy ktorý:**
|
||
- `.unwrap_or(hodnota)` — fallback je jednoduchá konštanta
|
||
- `.unwrap_or_default()` — chceš default pre daný typ (0, "", Vec::new(), false)
|
||
- `.unwrap_or_else(|| ...)` — fallback je drahý výpočet
|
||
|
||
---
|
||
|
||
### Úlohy G
|
||
|
||
**G-1.** Prepíš bez `match`:
|
||
```rust
|
||
fn zdvojnasob_option(x: Option<i32>) -> Option<i32> {
|
||
match x {
|
||
Some(n) => Some(n * 2),
|
||
None => None,
|
||
}
|
||
}
|
||
```
|
||
|
||
**G-2.** Prepíš bez `match`:
|
||
```rust
|
||
fn parsuj_prvy_znak(text: Option<String>) -> Option<char> {
|
||
match text {
|
||
Some(t) => {
|
||
match t.chars().next() {
|
||
Some(c) => Some(c),
|
||
None => None,
|
||
}
|
||
}
|
||
None => None,
|
||
}
|
||
}
|
||
```
|
||
|
||
**G-3.** Napíš funkciu `bezpecne_delenie(a: f64, b: f64) -> Option<f64>` — vráti `None` ak `b == 0.0`. Bez `if`, použi `.filter()` alebo podmienku v `.and_then()`.
|
||
|
||
Potom napíš: `delenie_s_fallbackom(a: f64, b: f64) -> f64` — ak sa nedá deliť, vráti `0.0`. Použi predošlú funkciu + `.unwrap_or()`.
|
||
|
||
**G-4.** Máš:
|
||
```rust
|
||
fn najdi_pouzivatela(id: u32) -> Option<String> { /* ... */ }
|
||
fn najdi_email(meno: &str) -> Option<String> { /* ... */ }
|
||
```
|
||
Napíš funkciu `email_pre_id(id: u32) -> Option<String>` — nájdi použivateľa podľa id, potom jeho email. Jeden riadok s `.and_then()`.
|
||
|
||
**G-5.** Čo vypíše? Najprv tipni, potom over:
|
||
```rust
|
||
let a: Option<i32> = Some(5);
|
||
println!("{:?}", a.map(|x| x > 3));
|
||
println!("{:?}", a.filter(|x| *x > 3));
|
||
println!("{:?}", a.filter(|x| *x > 10));
|
||
println!("{:?}", a.map(|x| x.to_string()));
|
||
println!("{:?}", a.and_then(|x| if x > 3 { Some(x * 2) } else { None }));
|
||
```
|
||
|
||
---
|
||
|
||
## Časť H: Kombinované cvičenia (simulácia skúšky)
|
||
|
||
### H-1. Kompletný CRUD
|
||
|
||
Implementuj bez pozerania na čokoľvek:
|
||
|
||
```rust
|
||
use serde::{Serialize, Deserialize};
|
||
use std::collections::HashMap;
|
||
|
||
#[derive(Serialize, Deserialize, Clone)]
|
||
struct Produkt {
|
||
nazov: String,
|
||
cena: f64,
|
||
kategoria: String,
|
||
na_sklade: bool,
|
||
}
|
||
|
||
#[derive(Serialize, Deserialize, Default)]
|
||
struct Obchod {
|
||
produkty: Vec<Produkt>,
|
||
}
|
||
|
||
impl Obchod {
|
||
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Obchod> { todo!() }
|
||
fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool { todo!() }
|
||
|
||
fn pridaj(&mut self, produkt: Produkt) -> Result<(), ()> { todo!() }
|
||
// duplicita podľa nazvu
|
||
|
||
fn odstran(&mut self, nazov: &str) -> Result<Produkt, ()> { todo!() }
|
||
|
||
fn najdi(&self, nazov: &str) -> Option<&Produkt> { todo!() }
|
||
|
||
fn na_sklade(&self) -> Vec<&Produkt> { todo!() }
|
||
|
||
fn v_kategorii(&self, kat: &str) -> Vec<&Produkt> { todo!() }
|
||
|
||
fn najdrahsi(&self) -> Option<&Produkt> { todo!() }
|
||
|
||
fn priemerny_cena_v_kategorii(&self, kat: &str) -> Option<f64> { todo!() }
|
||
|
||
fn pocty_kategorii(&self) -> HashMap<String, usize> { todo!() }
|
||
}
|
||
```
|
||
|
||
### H-2. Display enum + struct
|
||
|
||
```rust
|
||
use std::fmt;
|
||
|
||
enum Velkost { Mala, Stredna, Velka }
|
||
// Implementuj Display: "S", "M", "L"
|
||
|
||
struct Tricko { farba: String, velkost: Velkost, cena: f64 }
|
||
// Implementuj Display: "Tričko [M] červené - 19.99€"
|
||
```
|
||
|
||
---
|
||
|
||
---
|
||
|
||
---
|
||
|
||
# RIEŠENIA
|
||
|
||
## D-1
|
||
|
||
```rust
|
||
let cisla = vec![3, -1, 4, -1, 5, 9, -2, 6];
|
||
|
||
// a)
|
||
let pocet_kladnych: usize = cisla.iter().filter(|c| **c > 0).count();
|
||
// 5
|
||
|
||
// b)
|
||
let sucet_zapornych: i32 = cisla.iter().filter(|c| **c < 0).sum();
|
||
// -4
|
||
|
||
// c)
|
||
let kladne: Vec<i32> = cisla.iter().filter(|c| **c > 0).copied().collect();
|
||
// [3, 4, 5, 9, 6]
|
||
|
||
// d)
|
||
let vsetky_nad_minus5: bool = cisla.iter().all(|c| *c > -5);
|
||
// true
|
||
|
||
// e)
|
||
let existuje_nad_8: bool = cisla.iter().any(|c| *c > 8);
|
||
// true
|
||
```
|
||
|
||
## D-2
|
||
|
||
```rust
|
||
struct Student { meno: String, vek: u8, priemer: f32 }
|
||
|
||
// a)
|
||
let mena: Vec<&String> = studenti.iter().filter(|s| s.priemer < 2.0).map(|s| &s.meno).collect();
|
||
|
||
// b)
|
||
let najstarsi: Option<&Student> = studenti.iter().max_by_key(|s| s.vek);
|
||
|
||
// c)
|
||
fn priemerny_vek(studenti: &[Student]) -> Option<f64> {
|
||
if studenti.is_empty() { return None; }
|
||
let sucet: u32 = studenti.iter().map(|s| s.vek as u32).sum();
|
||
Some(sucet as f64 / studenti.len() as f64)
|
||
}
|
||
|
||
// d)
|
||
let ma_mladsiho: bool = studenti.iter().any(|s| s.vek < 18);
|
||
|
||
// e)
|
||
fn odstran_studenta(studenti: &mut Vec<Student>, meno: &str) -> Result<Student, ()> {
|
||
let pos = studenti.iter().position(|s| s.meno == meno).ok_or(())?;
|
||
Ok(studenti.remove(pos))
|
||
}
|
||
```
|
||
|
||
## D-3
|
||
|
||
```rust
|
||
fn kladne_zdvojnasobene(cisla: &[i32]) -> Vec<i32> {
|
||
cisla.iter().filter(|c| **c > 0).map(|c| c * 2).collect()
|
||
}
|
||
```
|
||
|
||
## D-4
|
||
|
||
`iter()` dáva `&i32` (nemeniteľná referencia). Nemôžeš cez ňu meniť. Treba `iter_mut()`:
|
||
|
||
```rust
|
||
fn zvys_vsetky(cisla: &mut Vec<i32>) {
|
||
for c in cisla.iter_mut() {
|
||
*c += 1;
|
||
}
|
||
}
|
||
```
|
||
|
||
## E-1
|
||
|
||
```rust
|
||
let cisla = vec![1, 5, 12, 3, 15, 8];
|
||
|
||
// a) pomenovaná funkcia
|
||
fn je_viac_ako_10(c: &&i32) -> bool { **c > 10 }
|
||
let vysledok: Vec<&i32> = cisla.iter().filter(je_viac_ako_10).collect();
|
||
|
||
// b) plný zápis
|
||
let vysledok: Vec<&i32> = cisla.iter().filter(|c: &&i32| -> bool { **c > 10 }).collect();
|
||
|
||
// c) najkratší
|
||
let vysledok: Vec<&i32> = cisla.iter().filter(|c| **c > 10).collect();
|
||
```
|
||
|
||
## E-2
|
||
|
||
Treba `move` — `faktor` je lokálna premenná, closure ho musí vlastniť:
|
||
|
||
```rust
|
||
fn vrat_nasobicku(faktor: i32) -> impl Fn(i32) -> i32 {
|
||
move |x| x * faktor
|
||
}
|
||
```
|
||
|
||
## E-3
|
||
|
||
```rust
|
||
fn aplikuj_na_vsetky(cisla: &[i32], f: impl Fn(i32) -> i32) -> Vec<i32> {
|
||
cisla.iter().map(|c| f(*c)).collect()
|
||
}
|
||
|
||
fn main() {
|
||
let cisla = vec![1, -2, 3, -4, 5];
|
||
|
||
// a)
|
||
let a = aplikuj_na_vsetky(&cisla, |x| x * 2);
|
||
|
||
// b)
|
||
let b = aplikuj_na_vsetky(&cisla, |x| x + 100);
|
||
|
||
// c)
|
||
fn absolutna(x: i32) -> i32 { x.abs() }
|
||
let c = aplikuj_na_vsetky(&cisla, absolutna);
|
||
}
|
||
```
|
||
|
||
## E-4
|
||
|
||
```rust
|
||
fn main() {
|
||
let mut zoznam = Vec::new();
|
||
let mut pridaj = |s: &str| zoznam.push(s.to_string());
|
||
pridaj("ahoj");
|
||
pridaj("svet");
|
||
pridaj("rust");
|
||
println!("{:?}", zoznam); // ["ahoj", "svet", "rust"]
|
||
}
|
||
```
|
||
|
||
## F-1
|
||
|
||
```rust
|
||
fn pocitaj_znaky(text: &str) -> HashMap<char, usize> {
|
||
let mut mapa = HashMap::new();
|
||
for c in text.chars() {
|
||
*mapa.entry(c).or_insert(0) += 1;
|
||
}
|
||
mapa
|
||
}
|
||
```
|
||
|
||
## F-2
|
||
|
||
```rust
|
||
struct Student { meno: String, trieda: String, priemer: f32 }
|
||
|
||
// a)
|
||
fn pocet_v_triedach(studenti: &[Student]) -> HashMap<String, usize> {
|
||
let mut mapa = HashMap::new();
|
||
for s in studenti {
|
||
*mapa.entry(s.trieda.clone()).or_insert(0) += 1;
|
||
}
|
||
mapa
|
||
}
|
||
|
||
// b)
|
||
fn najlepsi_v_triede<'a>(studenti: &'a [Student]) -> HashMap<String, &'a Student> {
|
||
let mut mapa: HashMap<String, &Student> = HashMap::new();
|
||
for s in studenti {
|
||
mapa.entry(s.trieda.clone())
|
||
.and_modify(|najlepsi| {
|
||
if s.priemer < najlepsi.priemer {
|
||
*najlepsi = s;
|
||
}
|
||
})
|
||
.or_insert(s);
|
||
}
|
||
mapa
|
||
}
|
||
```
|
||
|
||
## F-3
|
||
|
||
```rust
|
||
fn invertuj_mapu(mapa: &HashMap<String, String>) -> HashMap<String, String> {
|
||
let mut nova = HashMap::new();
|
||
for (k, v) in mapa {
|
||
nova.insert(v.clone(), k.clone());
|
||
}
|
||
nova
|
||
}
|
||
```
|
||
|
||
## G-1
|
||
|
||
```rust
|
||
fn zdvojnasob_option(x: Option<i32>) -> Option<i32> {
|
||
x.map(|n| n * 2)
|
||
}
|
||
```
|
||
|
||
## G-2
|
||
|
||
```rust
|
||
fn parsuj_prvy_znak(text: Option<String>) -> Option<char> {
|
||
text.and_then(|t| t.chars().next())
|
||
}
|
||
```
|
||
|
||
## G-3
|
||
|
||
```rust
|
||
fn bezpecne_delenie(a: f64, b: f64) -> Option<f64> {
|
||
Some(b).filter(|b| *b != 0.0).map(|b| a / b)
|
||
}
|
||
|
||
fn delenie_s_fallbackom(a: f64, b: f64) -> f64 {
|
||
bezpecne_delenie(a, b).unwrap_or(0.0)
|
||
}
|
||
```
|
||
|
||
## G-4
|
||
|
||
```rust
|
||
fn email_pre_id(id: u32) -> Option<String> {
|
||
najdi_pouzivatela(id).and_then(|meno| najdi_email(&meno))
|
||
}
|
||
```
|
||
|
||
## G-5
|
||
|
||
```
|
||
Some(true) // map: 5 > 3 = true, zabalí do Some
|
||
Some(5) // filter: 5 > 3 je true, nechá Some(5)
|
||
None // filter: 5 > 10 je false, vráti None
|
||
Some("5") // map: 5.to_string() = "5"
|
||
Some(10) // and_then: 5 > 3, tak Some(5 * 2) = Some(10)
|
||
```
|
||
|
||
## H-1
|
||
|
||
```rust
|
||
use serde::{Serialize, Deserialize};
|
||
use std::collections::HashMap;
|
||
|
||
#[derive(Serialize, Deserialize, Clone)]
|
||
struct Produkt {
|
||
nazov: String,
|
||
cena: f64,
|
||
kategoria: String,
|
||
na_sklade: bool,
|
||
}
|
||
|
||
#[derive(Serialize, Deserialize, Default)]
|
||
struct Obchod {
|
||
produkty: Vec<Produkt>,
|
||
}
|
||
|
||
impl Obchod {
|
||
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Obchod> {
|
||
let raw = std::fs::read_to_string(cesta).ok()?;
|
||
serde_json::from_str(&raw).ok()
|
||
}
|
||
|
||
fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool {
|
||
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
||
return false;
|
||
};
|
||
std::fs::write(cesta, json).is_ok()
|
||
}
|
||
|
||
fn pridaj(&mut self, produkt: Produkt) -> Result<(), ()> {
|
||
if self.produkty.iter().any(|p| p.nazov == produkt.nazov) {
|
||
return Err(());
|
||
}
|
||
self.produkty.push(produkt);
|
||
Ok(())
|
||
}
|
||
|
||
fn odstran(&mut self, nazov: &str) -> Result<Produkt, ()> {
|
||
let pos = self.produkty.iter().position(|p| p.nazov == nazov).ok_or(())?;
|
||
Ok(self.produkty.remove(pos))
|
||
}
|
||
|
||
fn najdi(&self, nazov: &str) -> Option<&Produkt> {
|
||
self.produkty.iter().find(|p| p.nazov == nazov)
|
||
}
|
||
|
||
fn na_sklade(&self) -> Vec<&Produkt> {
|
||
self.produkty.iter().filter(|p| p.na_sklade).collect()
|
||
}
|
||
|
||
fn v_kategorii(&self, kat: &str) -> Vec<&Produkt> {
|
||
self.produkty.iter().filter(|p| p.kategoria == kat).collect()
|
||
}
|
||
|
||
fn najdrahsi(&self) -> Option<&Produkt> {
|
||
self.produkty.iter().max_by(|a, b| a.cena.partial_cmp(&b.cena).unwrap())
|
||
}
|
||
|
||
fn priemerna_cena_v_kategorii(&self, kat: &str) -> Option<f64> {
|
||
let v_kat: Vec<&Produkt> = self.v_kategorii(kat);
|
||
if v_kat.is_empty() { return None; }
|
||
let sucet: f64 = v_kat.iter().map(|p| p.cena).sum();
|
||
Some(sucet / v_kat.len() as f64)
|
||
}
|
||
|
||
fn pocty_kategorii(&self) -> HashMap<String, usize> {
|
||
let mut mapa = HashMap::new();
|
||
for p in &self.produkty {
|
||
*mapa.entry(p.kategoria.clone()).or_insert(0) += 1;
|
||
}
|
||
mapa
|
||
}
|
||
}
|
||
```
|
||
|
||
## H-2
|
||
|
||
```rust
|
||
use std::fmt;
|
||
|
||
enum Velkost { Mala, Stredna, Velka }
|
||
|
||
impl fmt::Display for Velkost {
|
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
match self {
|
||
Velkost::Mala => write!(f, "S"),
|
||
Velkost::Stredna => write!(f, "M"),
|
||
Velkost::Velka => write!(f, "L"),
|
||
}
|
||
}
|
||
}
|
||
|
||
struct Tricko { farba: String, velkost: Velkost, cena: f64 }
|
||
|
||
impl fmt::Display for Tricko {
|
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
write!(f, "Tričko [{}] {} - {:.2}€", self.velkost, self.farba, self.cena)
|
||
}
|
||
}
|
||
```
|