Compare commits

..

15 Commits

Author SHA1 Message Date
Priec
48426abb5c uprava rng 2026-03-06 08:02:25 +01:00
Priec
edcd19c8c4 zaklady 2026-03-06 07:56:33 +01:00
Priec
a00d00070e rng a io 2026-03-06 01:36:58 +01:00
Priec
aed169f828 io 2026-03-05 21:08:21 +01:00
Priec
508c97cc9c impl things 2026-03-05 12:09:44 +01:00
Priec
c9b1b60ac1 clap2 2026-03-05 00:35:59 +01:00
Priec
8be1423b55 clap 2026-03-04 17:47:14 +01:00
Priec
3078efd928 serde json 2026-03-04 15:55:29 +01:00
Priec
5182e2f833 str 2026-02-28 23:21:09 +01:00
Priec
ce6b9b01d9 itertools 2026-02-27 17:59:06 +01:00
Priec
fac12bc665 btreemap and hashset 2026-02-26 18:59:33 +01:00
Priec
cea388f9e1 uprava priprava4{ 2026-02-22 18:45:04 +01:00
Priec
d26cd97aa9 priprava 4 2026-02-21 18:52:22 +01:00
Priec
4b452112e9 prekopana priprava 2 2026-02-16 19:50:35 +01:00
Priec
7fe89b0361 trening 2026-02-15 23:56:52 +01:00
23 changed files with 9903 additions and 3 deletions

View File

@@ -6,10 +6,26 @@ version = 4
name = "JR-priprava-na-skusku6"
version = "0.1.0"
dependencies = [
"itertools",
"serde",
"serde_json",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.17"

View File

@@ -4,5 +4,6 @@ version = "0.1.0"
edition = "2024"
[dependencies]
itertools = "0.14.0"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"

View File

@@ -1,3 +1,5 @@
pub mod priprava3;
use serde::Deserialize;
use serde::Serialize;
use std::fmt;
@@ -78,6 +80,30 @@ impl Filmoteka {
result
}
fn nacitaj(cesta: &str) -> Result<String, std::io::Error> {
std::fs::read_to_string(cesta)
}
fn nacitaj2(cesta: &str) -> bool {
std::fs::read_to_string(cesta).is_ok()
}
fn nacitaj3(cesta: &str) -> Result<String, String> {
match std::fs::read_to_string(cesta) {
Ok(obsah) => Ok(obsah),
Err(err) => Err(err.to_string()),
}
}
fn parsuj_cislo(text: &str) -> Option<i32> {
let x = text.parse::<i32>();
x.ok()
}
fn parsuj_cislo2(text: &str) -> Option<i32> {
text.parse::<i32>().ok()
}
pub fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
@@ -86,3 +112,22 @@ impl Filmoteka {
}
}
impl Film {
pub fn new(
nazov: &str,
reziser: &str,
rok: u16,
zaner: Zaner,
hodnotenie: f32
) -> Option<Self> {
if hodnotenie < 0.0 || hodnotenie > 10.0 {
return None;
};
return Some(Self {
nazov: nazov.to_string(),
reziser: reziser.to_string(),
rok, zaner, hodnotenie
})
}
}

View File

@@ -1,5 +1,22 @@
use std::collections::BTreeMap;
use itertools::Itertools;
use std::cmp::Reverse;
use std::collections::HashSet;
use std::collections::HashMap;
fn main() {
println!("Hello, world!");
// priprava3::spusti();
let input = vec![1, 3, 2, 1, 2, 3, 1, 1, 2];
let sorted = input.iter().sorted().collect::<Vec<_>>();
let mut vys: HashMap<i32, usize> = HashMap::new();
for (i, j) in &sorted.into_iter().chunk_by(|&x| x) {
// vys.insert(i, j);
print!("{:#?}", i);
let r = j.cloned().collect::<Vec<_>>();
vys.insert(*i, r.len());
print!("{:#?}", r);
}
println!("{:#?}", vys)
}

View File

@@ -0,0 +1,75 @@
use std::fmt;
use std::collections::HashMap;
pub fn spusti() {
let z1 = Zamestnanec::new("Meno", "1 oddelenie", 1000);
let z2 = Zamestnanec::new("Meno2", "2 oddelenie", 1300);
let z3 = Zamestnanec::new("Meno3", "2 oddelenie", 1100);
let z: &[Zamestnanec] = &[z1, z2, z3];
// celkovy_plat_na_oddeleni(z);
// pocet_na_oddeleni(z);
// vypis_statistiky2(z);
oddelenie_s_najvyssim_platom(z);
}
#[derive(Debug)]
pub struct Zamestnanec { meno: String, oddelenie: String, plat: u32 }
impl Zamestnanec {
pub fn new(meno: &str, oddelenie: &str, plat: u32) -> Self {
Self {
meno: meno.to_string(),
oddelenie: oddelenie.to_string(),
plat
}
}
}
pub fn pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, usize> {
let mut mapa: HashMap<String, usize> = HashMap::new();
// mapa.entry(zamestnanci).and_modify(|counter| *counter += 1).or_insert(1);
// println!("{:#?}", zamestnanci);
for x in zamestnanci {
mapa.entry(x.oddelenie.clone()).and_modify(|counter| *counter += 1).or_insert(1);
}
// println!("{:#?}", mapa);
mapa
}
pub fn celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, u32> {
let mut mapa: HashMap<String, u32> = HashMap::new();
for x in zamestnanci {
mapa.entry(x.oddelenie.clone()).and_modify(|c| *c += x.plat).or_insert(x.plat);
}
// println!("{:#?}", mapa);
mapa
}
pub fn vypis_statistiky(zamestnanci: &[Zamestnanec]) {
let mut mapa: HashMap<String, (usize, u32)> = HashMap::new();
for x in zamestnanci {
mapa.entry(x.oddelenie.clone()).and_modify(|(count, total)| {
*count += 1;
*total += x.plat;
}).or_insert((1, x.plat));
}
}
pub fn vypis_statistiky2(zamestnanci: &[Zamestnanec]) {
let pocet = pocet_na_oddeleni(zamestnanci);
let plat = celkovy_plat_na_oddeleni(zamestnanci);
for oddelenie in pocet.keys() {
println!("Pocet ludi:{:#?}, a ich plat: {:#?}", *pocet.get(oddelenie).unwrap_or(&0), *plat.get(oddelenie).unwrap_or(&0));
}
println!("{:#?}, {:#?}", pocet, plat);
}
pub fn oddelenie_s_najvyssim_platom(zamestnanci: &[Zamestnanec]) -> Option<String> {
let plat = celkovy_plat_na_oddeleni(zamestnanci);
let max = plat.iter().max_by_key(|x| *x);
println!("{:#?}", max);
match max {
Some(x) => return Some(x.0.clone()),
None => None,
}
}

956
priprava/rust_priprava10.md Normal file
View File

@@ -0,0 +1,956 @@
# clap — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
`Cargo.toml`:
```toml
[dependencies]
clap = { version = "4", features = ["derive"] }
```
```rust
use clap::Parser;
```
---
## Kapitola 1: Čo je clap
clap je knižnica na spracovanie **argumentov príkazového riadku** (CLI argumentov).
Keď spustíš program z terminálu:
```bash
./moj_program --subor data.json --pocet 5 --verbose
```
clap ti tie argumenty automaticky sparsuje do Rust štruktúry.
```rust
use clap::Parser;
#[derive(Parser)]
struct Argumenty {
#[arg(short, long)]
subor: String,
#[arg(short, long)]
pocet: u32,
#[arg(short, long)]
verbose: bool,
}
fn main() {
let args = Argumenty::parse();
println!("Súbor: {}", args.subor);
println!("Počet: {}", args.pocet);
println!("Verbose: {}", args.verbose);
}
```
Spustenie:
```bash
./moj_program --subor data.json --pocet 5 --verbose
# Súbor: data.json
# Počet: 5
# Verbose: true
./moj_program -s data.json -p 5 -v
# To isté — short varianty
./moj_program --help
# Automaticky vygenerovaná nápoveda!
```
**Čo clap robí za teba:**
- Parsuje argumenty z príkazového riadku
- Validuje typy (ak --pocet nie je číslo → chybová hláška)
- Generuje `--help` automaticky
- Generuje chybové hlášky ak niečo chýba
- Podporuje short (`-s`) aj long (`--subor`) verzie
### Úlohy 1
**1a.** Čo je CLI argument? Uveď 3 príklady programov, ktoré berú argumenty (napr. `ls -la`).
**1b.** Napíš štruktúru `Args` s jedným argumentom `meno: String` (long + short). Sparsuj a vypíš.
**1c.** Čo sa stane keď spustíš program bez argumentov? Čo keď s `--help`?
---
## Kapitola 2: Typy argumentov
### Povinný argument
```rust
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: String, // POVINNÝ — program spadne ak chýba
}
```
```bash
./program --subor data.json # OK
./program # CHYBA: "required arguments were not provided"
```
### Voliteľný argument (Option)
```rust
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: Option<String>, // VOLITEĽNÝ — None ak neuvedený
}
```
```bash
./program --subor data.json # args.subor = Some("data.json")
./program # args.subor = None
```
### Bool flag
```rust
#[derive(Parser)]
struct Args {
#[arg(short, long)]
verbose: bool, // false ak neuvedený, true ak uvedený
}
```
```bash
./program --verbose # args.verbose = true
./program -v # args.verbose = true
./program # args.verbose = false
```
Bool flag neberá hodnotu! Stačí ho uviesť a je true.
### Argument s default hodnotou
```rust
#[derive(Parser)]
struct Args {
#[arg(short, long, default_value = "data.json")]
subor: String,
#[arg(short, long, default_value_t = 10)]
pocet: u32,
}
```
```bash
./program # subor = "data.json", pocet = 10
./program --subor iny.json # subor = "iny.json", pocet = 10
./program -p 5 # subor = "data.json", pocet = 5
```
**Pozor:** `default_value` je pre String (vždy v úvodzovkách). `default_value_t` je pre iné typy (čísla, bool).
### Úlohy 2
**2a.** Napíš Args s:
- `subor: String` — povinný
- `vystup: Option<String>` — voliteľný
- `pocet: u32` — default 5
- `verbose: bool` — flag
Vyskúšaj rôzne kombinácie spustenia.
**2b.** Čo sa stane ak zadáš `--pocet ahoj` (nie číslo)?
**2c.** Čo je rozdiel medzi `Option<String>` a `String` s `default_value`?
---
## Kapitola 3: short a long
### Automatické short a long
```rust
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: String,
// --subor hodnota (long = názov poľa)
// -s hodnota (short = prvé písmeno)
}
```
### Len long (bez short)
```rust
#[derive(Parser)]
struct Args {
#[arg(long)]
konfig: String,
// --konfig hodnota OK
// -k hodnota NEFUNGUJE
}
```
### Len short (bez long)
```rust
#[derive(Parser)]
struct Args {
#[arg(short)]
verbose: bool,
// -v OK
// --verbose NEFUNGUJE
}
```
### Vlastné short/long
```rust
#[derive(Parser)]
struct Args {
#[arg(short = 'f', long = "file")]
subor: String,
// -f hodnota OK
// --file hodnota OK
// --subor hodnota NEFUNGUJE (premenovali sme)
}
```
### Kolízia short písmen
```rust
// PROBLÉM: obe majú short 's'
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: String, // -s
#[arg(short, long)]
stav: String, // -s KOLÍZIA!
}
// Riešenie: jedného premenuj
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: String, // -s
#[arg(short = 't', long)]
stav: String, // -t
}
```
### Úlohy 3
**3a.** Napíš Args kde:
- `input` má short `-i` a long `--input`
- `output` má short `-o` a long `--output`
- `debug` má len short `-d` (žiadny long)
- `format` má len long `--format` (žiadny short)
**3b.** Máš 3 polia: `subor`, `stav`, `slovo`. Všetky začínajú na 's'. Vyrieš kolíziu short písmen.
---
## Kapitola 4: Pozičné argumenty
Doteraz sme používali pomenované argumenty (`--subor`, `-s`). Existujú aj pozičné — bez mena, len podľa poradia.
```rust
#[derive(Parser)]
struct Args {
/// Vstupný súbor
vstup: String, // PRVÝ pozičný argument
/// Výstupný súbor
vystup: String, // DRUHÝ pozičný argument
#[arg(short, long)]
verbose: bool, // Pomenovaný (flag)
}
```
```bash
./program input.json output.json --verbose
# ^^^^^^^^^^ ^^^^^^^^^^^
# prvý druhý pozičný
```
**Pravidlo:** Ak pole nemá `#[arg(short, long)]`, je pozičné.
### Voliteľný pozičný
```rust
#[derive(Parser)]
struct Args {
vstup: String, // povinný pozičný
vystup: Option<String>, // voliteľný pozičný
}
```
```bash
./program input.json # vystup = None
./program input.json output.json # vystup = Some("output.json")
```
### Pozičný s default
```rust
#[derive(Parser)]
struct Args {
#[arg(default_value = "data.json")]
subor: String,
}
```
```bash
./program # subor = "data.json"
./program iny.json # subor = "iny.json"
```
### Úlohy 4
**4a.** Napíš Args pre program, ktorý kopíruje súbor:
```bash
./kopiruj zdrojovy.txt cielovy.txt
```
Dva pozičné argumenty.
**4b.** Napíš Args pre grep-like program:
```bash
./hladaj "hľadaný text" subor.txt --ignore-case
```
Pozičné: vzor a súbor. Pomenované: ignore_case (bool flag).
---
## Kapitola 5: Popis a help text
### Dokumentačné komentáre → help text
```rust
/// Správca knižnice — CLI nástroj na správu kníh
#[derive(Parser)]
struct Args {
/// Cesta k JSON súboru s dátami
#[arg(short, long)]
subor: String,
/// Počet výsledkov na stránku
#[arg(short, long, default_value_t = 10)]
pocet: u32,
/// Zapni podrobný výpis
#[arg(short, long)]
verbose: bool,
}
```
```bash
./program --help
# Správca knižnice — CLI nástroj na správu kníh
#
# Usage: program [OPTIONS] --subor <SUBOR>
#
# Options:
# -s, --subor <SUBOR> Cesta k JSON súboru s dátami
# -p, --pocet <POCET> Počet výsledkov na stránku [default: 10]
# -v, --verbose Zapni podrobný výpis
# -h, --help Print help
```
**Pravidlo:** `///` komentár nad štruktúrou = popis programu. `///` nad poľom = popis argumentu. clap ich automaticky zobrazí v `--help`.
### about a help atribúty
```rust
#[derive(Parser)]
#[command(about = "Správca knižnice", version = "1.0")]
struct Args {
#[arg(short, long, help = "Cesta k súboru")]
subor: String,
}
```
```bash
./program --version
# program 1.0
```
### Úlohy 5
**5a.** Pridaj ku každému argumentu z úlohy 2a popis (`///` komentár). Spusti s `--help` a pozri výstup.
**5b.** Pridaj k štruktúre `#[command(about, version)]`. Vyskúšaj `--help` a `--version`.
---
## Kapitola 6: Subcommands — podpríkazy
Väčšie programy majú podpríkazy:
```bash
git add file.txt
git commit -m "správa"
git push origin main
```
V clap to riešiš cez enum:
```rust
use clap::{Parser, Subcommand};
#[derive(Parser)]
struct Args {
#[command(subcommand)]
prikaz: Prikaz,
}
#[derive(Subcommand)]
enum Prikaz {
/// Pridaj novú knihu
Pridaj {
/// Názov knihy
nazov: String,
/// Autor knihy
autor: String,
},
/// Odstráň knihu podľa názvu
Odstran {
/// Názov knihy na odstránenie
nazov: String,
},
/// Vypíš všetky knihy
Vypis,
/// Hľadaj knihy
Hladaj {
/// Hľadaný výraz
vyraz: String,
},
}
fn main() {
let args = Args::parse();
match args.prikaz {
Prikaz::Pridaj { nazov, autor } => {
println!("Pridávam: {} od {}", nazov, autor);
}
Prikaz::Odstran { nazov } => {
println!("Odstraňujem: {}", nazov);
}
Prikaz::Vypis => {
println!("Výpis kníh...");
}
Prikaz::Hladaj { vyraz } => {
println!("Hľadám: {}", vyraz);
}
}
}
```
```bash
./program pridaj "Duna" "Frank Herbert"
./program odstran "Duna"
./program vypis
./program hladaj "Herbert"
./program --help
# Shows all subcommands
./program pridaj --help
# Shows help for "pridaj" subcommand
```
### Spoločné argumenty + subcommand
```rust
#[derive(Parser)]
struct Args {
/// Cesta k JSON súboru
#[arg(short, long, default_value = "data.json")]
subor: String,
#[command(subcommand)]
prikaz: Prikaz,
}
```
```bash
./program --subor moje.json pridaj "Duna" "Herbert"
# ^^^^^^^^^^^^^^^^^ ^^^^^^ spoločný arg pred subcommand
```
### Úlohy 6
**6a.** Napíš CLI pre správcu kontaktov:
```bash
./kontakty pridaj "Anna" "+421900000000"
./kontakty odstran "Anna"
./kontakty vypis
./kontakty hladaj "Ann"
```
**6b.** Pridaj spoločný argument `--subor` (default "kontakty.json") pred subcommand.
**6c.** Pridaj do `pridaj` subcomandu voliteľný argument `--email`.
---
## Kapitola 7: ValueEnum — enum ako argument
Ak chceš aby argument bol jedna z preddefinovaných hodnôt:
```rust
use clap::{Parser, ValueEnum};
#[derive(Clone, ValueEnum)]
enum Format {
Json,
Csv,
Text,
}
#[derive(Parser)]
struct Args {
#[arg(short, long, default_value = "json")]
format: Format,
}
fn main() {
let args = Args::parse();
match args.format {
Format::Json => println!("Výstup ako JSON"),
Format::Csv => println!("Výstup ako CSV"),
Format::Text => println!("Výstup ako text"),
}
}
```
```bash
./program --format json
./program --format csv
./program --format text
./program --format xml # CHYBA: "invalid value 'xml'"
./program --help
# -f, --format <FORMAT> [default: json] [possible values: json, csv, text]
```
clap automaticky:
- Ukáže povolené hodnoty v `--help`
- Odmietne neplatnú hodnotu
- Case-insensitive porovnanie
### Na skúške — enum Stav ako argument
```rust
#[derive(Clone, ValueEnum, Serialize, Deserialize, PartialEq, Debug)]
enum Stav {
Nova,
Pouzivana,
Poskodena,
Vyradena,
}
#[derive(Subcommand)]
enum Prikaz {
ZmenStav {
nazov: String,
#[arg(short, long)]
stav: Stav,
},
PodlaStavu {
#[arg(short, long)]
stav: Stav,
},
}
```
```bash
./program zmen-stav "Duna" --stav pouzivana
./program podla-stavu --stav nova
```
### Úlohy 7
**7a.** Vytvor enum `Priorita { Nizka, Stredna, Vysoka }` s `ValueEnum`. Použi ho ako argument v CLI.
**7b.** Pridaj do subcommandu `pridaj` argument `--priorita` typu Priorita s defaultom `Stredna`.
---
## Kapitola 8: Vec argumenty — viacero hodnôt
```rust
#[derive(Parser)]
struct Args {
/// Súbory na spracovanie
subory: Vec<String>, // 0 alebo viac pozičných
}
```
```bash
./program a.txt b.txt c.txt
# args.subory = ["a.txt", "b.txt", "c.txt"]
./program
# args.subory = []
```
### Minimálne 1
```rust
#[derive(Parser)]
struct Args {
/// Súbory na spracovanie (aspoň 1)
#[arg(required = true)]
subory: Vec<String>,
}
```
```bash
./program a.txt b.txt # OK
./program # CHYBA: required
```
### Pomenovaný s viacerými hodnotami
```rust
#[derive(Parser)]
struct Args {
#[arg(short, long)]
tagy: Vec<String>,
}
```
```bash
./program --tagy rust --tagy cli --tagy serde
# args.tagy = ["rust", "cli", "serde"]
./program -t rust -t cli -t serde
# to isté
```
### Úlohy 8
**8a.** Napíš CLI, ktoré berie zoznam čísel a vypočíta ich súčet:
```bash
./sucet 1 2 3 4 5
# Súčet: 15
```
Hint: pozičný `Vec<i32>`.
**8b.** Napíš CLI s pomenovaným argumentom `--vyluc` ktorý berie viacero mien. Potom vypíš "Vylúčení: Anna, Boris".
---
## Kapitola 9: Validácia argumentov
### value_parser — kontrola rozsahu
```rust
#[derive(Parser)]
struct Args {
/// Port (1-65535)
#[arg(short, long, value_parser = clap::value_parser!(u16).range(1..=65535))]
port: u16,
/// Počet (1-100)
#[arg(short, long, value_parser = clap::value_parser!(u32).range(1..=100))]
pocet: u32,
}
```
```bash
./program --port 8080 --pocet 50 # OK
./program --port 0 # CHYBA: "0 is not in 1..=65535"
./program --pocet 200 # CHYBA
```
### PathBuf argument
Na skúške cesta k súboru je vždy `PathBuf`:
```rust
use std::path::PathBuf;
#[derive(Parser)]
struct Args {
/// Cesta k dátovému súboru
#[arg(short, long, default_value = "data.json")]
subor: PathBuf,
}
fn main() {
let args = Args::parse();
// args.subor je PathBuf — môžeš ho priamo použiť
let kniznica = Kniznica::nacitaj_zo_suboru(&args.subor)
.unwrap_or_default();
}
```
```bash
./program --subor /cesta/k/suboru.json
./program -s data.json
./program # default: data.json
```
### Úlohy 9
**9a.** Napíš Args s argumentom `--vek` typu u8, s rozsahom 0 až 150.
**9b.** Napíš Args s PathBuf argumentom `--vstup` (povinný) a `--vystup` (voliteľný, default "output.json").
---
## Kapitola 10: Kompletný vzor zo skúšky
### Vzor: Knižnica CLI
```rust
use clap::{Parser, Subcommand, ValueEnum};
use serde::{Serialize, Deserialize};
use std::fs;
use std::path::PathBuf;
// === Typy ===
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ValueEnum)]
pub enum Stav {
Nova,
Pouzivana,
Poskodena,
Vyradena,
}
impl Default for Stav {
fn default() -> Self {
Stav::Nova
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Kniha {
pub nazov: String,
pub autor: String,
pub rok: u16,
pub stav: Stav,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Kniznica {
pub knihy: Vec<Kniha>,
}
// === serde I/O ===
impl Kniznica {
pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kniznica> {
let raw = fs::read_to_string(cesta).ok()?;
serde_json::from_str(&raw).ok()
}
pub fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
fs::write(cesta, json).is_ok()
}
pub fn pridaj(&mut self, kniha: Kniha) -> Result<(), String> {
if self.knihy.iter().any(|k| k.nazov == kniha.nazov) {
return Err(format!("Kniha '{}' už existuje", kniha.nazov));
}
self.knihy.push(kniha);
Ok(())
}
pub fn odstran(&mut self, nazov: &str) -> Result<Kniha, String> {
let pos = self.knihy.iter()
.position(|k| k.nazov == nazov)
.ok_or(format!("Kniha '{}' nenájdená", nazov))?;
Ok(self.knihy.remove(pos))
}
}
// === CLI ===
/// Správca knižnice
#[derive(Parser)]
#[command(about, version)]
struct Args {
/// Cesta k JSON súboru
#[arg(short, long, default_value = "kniznica.json")]
subor: PathBuf,
#[command(subcommand)]
prikaz: Prikaz,
}
#[derive(Subcommand)]
enum Prikaz {
/// Pridaj novú knihu
Pridaj {
/// Názov knihy
nazov: String,
/// Autor knihy
autor: String,
/// Rok vydania
#[arg(short, long)]
rok: u16,
/// Stav knihy
#[arg(short, long, default_value = "nova")]
stav: Stav,
},
/// Odstráň knihu
Odstran {
/// Názov knihy
nazov: String,
},
/// Vypíš všetky knihy
Vypis {
/// Filtrovať podľa stavu
#[arg(short, long)]
stav: Option<Stav>,
},
/// Hľadaj knihy podľa autora
Hladaj {
/// Meno autora
autor: String,
},
}
fn main() {
let args = Args::parse();
// Načítaj alebo vytvor prázdnu
let mut kniznica = Kniznica::nacitaj_zo_suboru(&args.subor)
.unwrap_or_default();
match args.prikaz {
Prikaz::Pridaj { nazov, autor, rok, stav } => {
let kniha = Kniha { nazov, autor, rok, stav };
match kniznica.pridaj(kniha) {
Ok(()) => println!("Kniha pridaná."),
Err(e) => println!("Chyba: {}", e),
}
}
Prikaz::Odstran { nazov } => {
match kniznica.odstran(&nazov) {
Ok(kniha) => println!("Odstránená: {} ({})", kniha.nazov, kniha.autor),
Err(e) => println!("Chyba: {}", e),
}
}
Prikaz::Vypis { stav } => {
let knihy: Vec<&Kniha> = match &stav {
Some(s) => kniznica.knihy.iter().filter(|k| &k.stav == s).collect(),
None => kniznica.knihy.iter().collect(),
};
if knihy.is_empty() {
println!("Žiadne knihy.");
} else {
for kniha in &knihy {
println!("{}{} ({}) [{:?}]", kniha.nazov, kniha.autor, kniha.rok, kniha.stav);
}
}
}
Prikaz::Hladaj { autor } => {
let najdene: Vec<&Kniha> = kniznica.knihy.iter()
.filter(|k| k.autor.to_lowercase().contains(&autor.to_lowercase()))
.collect();
if najdene.is_empty() {
println!("Nič nenájdené.");
} else {
for kniha in &najdene {
println!("{}{}", kniha.nazov, kniha.autor);
}
}
}
}
// Ulož
kniznica.uloz_do_suboru(&args.subor);
}
```
Spustenie:
```bash
./kniznica pridaj "Duna" "Frank Herbert" --rok 1965
./kniznica pridaj "Hobbit" "J.R.R. Tolkien" --rok 1937 --stav pouzivana
./kniznica vypis
./kniznica vypis --stav nova
./kniznica hladaj "Tolkien"
./kniznica odstran "Duna"
./kniznica --subor backup.json vypis
```
### Úlohy 10
**10a.** Implementuj kompletný CLI pre **Správcu úloh** (Task Manager):
```
Štruktúra: Uloha { nazov: String, priorita: Priorita, hotova: bool }
Enum: Priorita { Nizka, Stredna, Vysoka }
Subcommands:
- pridaj <nazov> --priorita <priorita>
- hotovo <nazov>
- odstran <nazov>
- vypis [--priorita <priorita>]
- statistiky (počet celkom, hotových, nehotových)
Spoločný: --subor (PathBuf, default "ulohy.json")
```
**10b.** Implementuj kompletný CLI pre **Milionár** kvíz:
```
Subcommands:
- pridaj-otazku <text> --odpovede <4 odpovede> --spravna <index>
- hraj
- vypis-otazky
Spoločný: --subor (PathBuf, default "otazky.json")
```
**10c.** Implementuj kompletný CLI pre **Adresár**:
```
Štruktúra: Kontakt { meno: String, telefon: String, email: Option<String> }
Subcommands:
- pridaj <meno> <telefon> [--email <email>]
- odstran <meno>
- hladaj <vyraz>
- vypis [--zoradit]
- export --format <json|csv>
Spoločný: --subor (PathBuf, default "kontakty.json")
```
---
## Prehľad: clap atribúty
| Atribút | Kde | Čo robí |
|---------|-----|---------|
| `#[derive(Parser)]` | štruktúra | Hlavná CLI štruktúra |
| `#[derive(Subcommand)]` | enum | Podpríkazy |
| `#[derive(ValueEnum)]` | enum | Enum ako hodnota argumentu |
| `#[command(subcommand)]` | pole | Toto pole je subcommand |
| `#[command(about, version)]` | štruktúra | Pridaj popis a verziu |
| `#[arg(short, long)]` | pole | Pomenovaný argument |
| `#[arg(short = 'x')]` | pole | Vlastné short písmeno |
| `#[arg(long = "nazov")]` | pole | Vlastný long názov |
| `#[arg(default_value = "...")]` | pole | Default pre String/PathBuf |
| `#[arg(default_value_t = N)]` | pole | Default pre čísla/bool |
| `#[arg(required = true)]` | pole | Vynúť aj Vec |
| `#[arg(value_parser = ...)]` | pole | Validácia |
| `/// komentár` | štruktúra/pole | Help text |
## Prehľad: typy polí
| Typ poľa | Správanie |
|-----------|-----------|
| `String` | Povinný argument |
| `Option<String>` | Voliteľný argument |
| `bool` | Flag (true/false) |
| `Vec<String>` | Viacero hodnôt |
| `PathBuf` | Cesta k súboru |
| `u32, i32, f64...` | Automaticky parsované |
| `MojEnum` (ValueEnum) | Povolené hodnoty |

268
priprava/rust_priprava11.md Normal file
View File

@@ -0,0 +1,268 @@
# clap → match → volanie metód — kde robíš chybu
Tvoj problém nie je clap. Clap máš dobre. Tvoj problém je toto:
```rust
Sc::DajKnihuIsbn { cesta, isbn } => {
Kniznica::daj_knihu_podla_isbn(isbn); // ZLE
}
```
Tri chyby na jednom riadku:
1. Nikde si nenačítal knižnicu zo súboru
2. Voláš metódu ako statickú (`Kniznica::`) namiesto na inštancii (`kniznica.`)
3. Ignoruješ návratovú hodnotu
---
## Pravidlo 1: Najprv načítaj, potom volaj
Metódy ako `daj_knihu_podla_isbn``&self` metódy — potrebujú inštanciu. Tú musíš najprv vytvoriť z JSON súboru.
```rust
// ZLE — Kniznica neexistuje, voláš na type
Kniznica::daj_knihu_podla_isbn(isbn);
// DOBRE — najprv načítaj, potom volaj na inštancii
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta).unwrap_or_default();
kniznica.daj_knihu_podla_isbn(&isbn);
```
---
## Pravidlo 2: Statické vs inštančné metódy
Pozri sa na signatúru metódy. To ti povie ako ju volať.
```rust
// STATICKÁ metóda — nemá self, volaj cez Typ::
fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kniznica>
// Volanie:
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta);
// &self metóda — len čítanie, volaj cez instancia.
fn daj_knihu_podla_isbn(&self, isbn: &str) -> Option<&Kniha>
// Volanie:
let vysledok = kniznica.daj_knihu_podla_isbn(&isbn);
// &mut self metóda — mení dáta, volaj cez mut instancia.
fn pridaj_knihu(&mut self, kniha: Kniha) -> Result<(), ()>
// Volanie:
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta).unwrap_or_default();
let vysledok = kniznica.pridaj_knihu(kniha);
```
Zhrnutie:
| Signatúra | Čo potrebuješ | Volanie |
|-----------|---------------|---------|
| `fn nieco(cesta: &PathBuf)` — bez self | nič | `Kniznica::nieco(&cesta)` |
| `fn nieco(&self, ...)` | `let kniznica = ...` | `kniznica.nieco(...)` |
| `fn nieco(&mut self, ...)` | `let mut kniznica = ...` | `kniznica.nieco(...)` |
---
## Pravidlo 3: Spracuj návratovú hodnotu
Každá metóda niečo vracia. Musíš s tým niečo urobiť.
```rust
// Option<&Kniha> — buď existuje, alebo nie
match kniznica.daj_knihu_podla_isbn(&isbn) {
Some(kniha) => println!("{}", kniha),
None => println!("Kniha nenájdená"),
}
// Vec<&Kniha> — môže byť prázdny
let knihy = kniznica.daj_knihy_autora(&autor);
if knihy.is_empty() {
println!("Žiadne knihy");
} else {
for kniha in &knihy {
println!("{}", kniha);
}
}
// Result<Kniha, ()> — podarilo sa alebo nie
match kniznica.odstran_knihu(&isbn) {
Ok(kniha) => println!("Odstránená: {}", kniha),
Err(()) => println!("Kniha nenájdená"),
}
```
---
## Pravidlo 4: Ak meníš dáta, ulož na konci
```rust
// Čítacie metódy (&self) — NEUKLADAJ
Sc::DajKnihuIsbn { cesta, isbn } => {
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta).unwrap_or_default();
// daj_knihu_podla_isbn je &self — len číta
match kniznica.daj_knihu_podla_isbn(&isbn) {
Some(kniha) => println!("{}", kniha),
None => println!("Nenájdená"),
}
// ŽIADNE uloženie — nič sa nezmenilo
}
// Meniace metódy (&mut self) — ULOŽ!
Sc::OdstranKnihu { cesta, isbn } => {
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta).unwrap_or_default();
// odstran_knihu je &mut self — mení dáta
match kniznica.odstran_knihu(&isbn) {
Ok(kniha) => {
println!("Odstránená: {}", kniha);
kniznica.uloz_do_suboru(&cesta); // ULOŽ ZMENY!
}
Err(()) => println!("Nenájdená"),
}
}
```
Keď uložiť:
- `pridaj_knihu` — áno (pridávaš)
- `odstran_knihu` — áno (mažeš)
- `daj_knihu_podla_isbn` — nie (len čítaš)
- `daj_knihy_autora` — nie
- `vypis_vydavatelstva_a_pocet_knih` — nie
---
## Pravidlo 5: &isbn vs isbn — referencie v argumentoch
Pozri sa čo metóda chce. Ak chce `&str`, daj referenciu na String.
```rust
// Metóda chce: isbn: &str
fn daj_knihu_podla_isbn(&self, isbn: &str) -> Option<&Kniha>
// Ty máš: isbn: String (z clapu)
// Volanie:
kniznica.daj_knihu_podla_isbn(&isbn) // & premení String na &str
```
Vždy keď metóda berie `&str` a ty máš `String`, daj `&`:
```rust
kniznica.daj_knihu_podla_isbn(&isbn); // String → &str
kniznica.daj_knihy_autora(&autor); // String → &str
kniznica.odstran_knihu(&isbn); // String → &str
kniznica.daj_knihy_podla_stavu(stav); // Stav sa predáva priamo (nie referencia)
```
---
## Tvoj kód opravený
```rust
use clap::{Parser, Subcommand};
use std::path::PathBuf;
use riesenie1::Kniznica;
#[derive(Parser, Debug)]
struct Args {
#[command(subcommand)]
cmd: Sc,
}
#[derive(Subcommand, Debug)]
pub enum Sc {
DajKnihuIsbn {
cesta: PathBuf,
isbn: String,
},
DajKnihyAutora {
cesta: PathBuf,
autor: String,
},
OdstranKnihu {
cesta: PathBuf,
isbn: String,
},
VypisVydavatelstva {
cesta: PathBuf,
},
}
fn main() {
let args = Args::parse();
match args.cmd {
Sc::DajKnihuIsbn { cesta, isbn } => {
// 1. Načítaj
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta).unwrap_or_default();
// 2. Zavolaj na inštancii
// 3. Spracuj výsledok
match kniznica.daj_knihu_podla_isbn(&isbn) {
Some(kniha) => println!("{}", kniha),
None => println!("Kniha s ISBN {} nenájdená", isbn),
}
}
Sc::DajKnihyAutora { cesta, autor } => {
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta).unwrap_or_default();
let knihy = kniznica.daj_knihy_autora(&autor);
if knihy.is_empty() {
println!("Žiadne knihy od autora {}", autor);
} else {
for kniha in &knihy {
println!("{}", kniha);
}
}
}
Sc::OdstranKnihu { cesta, isbn } => {
// mut! lebo odstran_knihu je &mut self
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta).unwrap_or_default();
match kniznica.odstran_knihu(&isbn) {
Ok(kniha) => {
println!("Odstránená: {}", kniha);
kniznica.uloz_do_suboru(&cesta); // ULOŽ!
}
Err(()) => println!("Kniha s ISBN {} nenájdená", isbn),
}
}
Sc::VypisVydavatelstva { cesta } => {
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta).unwrap_or_default();
// Táto metóda sama vypíše — len ju zavolaj
kniznica.vypis_vydavatelstva_a_pocet_knih();
}
}
}
```
---
## Cvičenie: Doplň match arms
Dopíš chýbajúce časti. Signatúry metód sú v komentároch.
```rust
// SIGNATÚRY:
// fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kontakty>
// fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool
// fn pridaj(&mut self, kontakt: Kontakt) -> Result<(), ()>
// fn odstran(&mut self, meno: &str) -> Result<Kontakt, ()>
// fn hladaj(&self, meno: &str) -> Option<&Kontakt>
// fn vsetky(&self) -> Vec<&Kontakt>
match args.cmd {
Sc::Pridaj { cesta, meno, telefon } => {
// TODO: načítaj (mut!), vytvor Kontakt, pridaj, ulož ak ok
}
Sc::Odstran { cesta, meno } => {
// TODO: načítaj (mut!), odstráň, vypíš výsledok, ulož ak ok
}
Sc::Hladaj { cesta, meno } => {
// TODO: načítaj, hľadaj, vypíš Some/None
}
Sc::Vypis { cesta } => {
// TODO: načítaj, zavolaj vsetky(), iteruj a vypíš
}
}
```
Skús to vyplniť sám. Ak nevieš, pozri na opravený kód vyššie — vzor je vždy rovnaký:
1. `let (mut) x = Typ::nacitaj_zo_suboru(&cesta).unwrap_or_default();`
2. `let vysledok = x.metoda(&arg);`
3. `match/if/for` na výsledku
4. Ak `&mut self` metóda → `x.uloz_do_suboru(&cesta);`

1133
priprava/rust_priprava12.md Normal file

File diff suppressed because it is too large Load Diff

820
priprava/rust_priprava13.md Normal file
View File

@@ -0,0 +1,820 @@
# I/O v Ruste — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
---
## Kapitola 1: println! a print! — výstup
```rust
// println! — vypíše + nový riadok
println!("Ahoj svet");
// Ahoj svet\n
// print! — vypíše BEZ nového riadku
print!("Zadaj meno: ");
// Zadaj meno: _ (kurzor zostane na tom istom riadku)
```
### Formátovanie
```rust
let meno = "Anna";
let vek = 25;
let priemer = 1.847;
// Základné
println!("{}{} rokov", meno, vek);
// Anna má 25 rokov
// Zaokrúhlenie na 2 desatinné
println!("Priemer: {:.2}", priemer);
// Priemer: 1.85
// Zarovnanie
println!("{:<10} | {:>5}", "Meno", "Vek");
println!("{:<10} | {:>5}", "Anna", 25);
println!("{:<10} | {:>5}", "Boris", 30);
// Meno | Vek
// Anna | 25
// Boris | 30
// Padding znakom
println!("{:-^20}", "MENU");
// --------MENU--------
// Debug výpis
let vektor = vec![1, 2, 3];
println!("{:?}", vektor); // [1, 2, 3]
println!("{:#?}", vektor); // pekný formát na viac riadkov
```
### eprintln! — chybový výstup
```rust
// eprintln! ide na stderr (nie stdout)
eprintln!("Chyba: súbor neexistuje");
// V praxi: normálne správy cez println!, chyby cez eprintln!
```
### Úlohy 1
**1a.** Vypíš tabuľku 3 produktov s názvom (zarovnaný vľavo na 15), cenou (zarovnaná vpravo na 8, 2 desatinné) a počtom (zarovnaný vpravo na 5):
```
Chlieb | 1.20 | 50
Mlieko | 0.89 | 120
Maslo | 2.49 | 30
```
**1b.** Máš Vec<(String, f64)> — mená a priemery. Vypíš ich zoradené podľa priemeru, formátované na 1 desatinné miesto.
---
## Kapitola 2: stdin — čítanie vstupu
```rust
use std::io;
fn main() {
// Základné čítanie riadku
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
// vstup teraz obsahuje text + '\n' na konci!
let vstup = vstup.trim(); // odstráň \n a medzery
println!("Napísal si: {}", vstup);
}
```
### Krok po kroku
```rust
// 1. Vytvor prázdny String (buffer)
let mut vstup = String::new();
// 2. Prečítaj riadok do bufferu
// read_line PRIDÁVA do stringu (neprepisuje!)
// Vracia Result<usize, io::Error> (počet prečítaných bajtov)
io::stdin().read_line(&mut vstup).unwrap();
// 3. VŽDY trimni — read_line pridáva '\n'
let vstup = vstup.trim();
```
### Čítanie čísla
```rust
fn nacitaj_cislo() -> Option<i32> {
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).ok()?;
vstup.trim().parse().ok()
}
// Použitie:
match nacitaj_cislo() {
Some(n) => println!("Číslo: {}", n),
None => println!("To nie je číslo"),
}
```
### Čítanie jedného znaku
```rust
fn nacitaj_znak() -> Option<char> {
let mut vstup = String::new();
std::io::stdin().read_line(&mut vstup).ok()?;
vstup.trim().chars().next()
}
// Použitie:
match nacitaj_znak() {
Some(c) => println!("Znak: {}", c),
None => println!("Prázdny vstup"),
}
```
### Pozor: read_line pridáva, neprepisuje!
```rust
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap(); // "ahoj\n"
io::stdin().read_line(&mut vstup).unwrap(); // "ahoj\nsvet\n" — PRIDALO!
// Ak chceš čítať viackrát, buď trimuj alebo vytvor nový String
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
let prvy = vstup.trim().to_string();
vstup.clear(); // VYMAŽ pred ďalším čítaním!
io::stdin().read_line(&mut vstup).unwrap();
let druhy = vstup.trim().to_string();
```
### Úlohy 2
**2a.** Napíš program, ktorý prečíta meno a vek od používateľa a vypíše `"Ahoj Anna, máš 25 rokov"`. Vek parsuj na u32.
**2b.** Napíš funkciu `nacitaj_riadok() -> Option<String>` — prečíta riadok, trimne, vráti None ak je prázdny.
**2c.** Napíš funkciu `nacitaj_float() -> Option<f64>` — prečíta riadok, trimne, parsne na f64.
**2d.** Napíš program, ktorý opakovane číta čísla od používateľa kým nezadá "koniec". Na konci vypíše súčet.
---
## Kapitola 3: Game loop — opakované čítanie
Na skúške je typický vzor: čítaj vstup v cykle, reaguj, skonči keď podmienka.
### Základný game loop
```rust
use std::io;
fn main() {
loop {
print!("Zadaj príkaz: ");
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
let vstup = vstup.trim();
match vstup {
"koniec" => {
println!("Zbohom!");
break;
}
"pomoc" => {
println!("Príkazy: koniec, pomoc");
}
_ => {
println!("Neznámy príkaz: {}", vstup);
}
}
}
}
```
### Game loop s parsovaním
```rust
loop {
print!("> ");
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
let casti: Vec<&str> = vstup.trim().split_whitespace().collect();
if casti.is_empty() { continue; }
match casti[0] {
"pridaj" => {
if casti.len() < 2 {
println!("Použitie: pridaj <meno>");
continue;
}
let meno = casti[1];
println!("Pridávam: {}", meno);
}
"vypis" => {
println!("Výpis...");
}
"koniec" => break,
_ => println!("Neznámy príkaz"),
}
}
```
### Úlohy 3
**3a.** Napíš jednoduchý kalkulátor. Používateľ zadáva `"5 + 3"`, `"10 - 4"`, `"koniec"`. Program počíta a vypisuje výsledok.
**3b.** Napíš program, kde používateľ opakovane háda číslo 1-100. Program povie "viac", "menej" alebo "správne". Použi `rand::Rng`.
---
## Kapitola 4: Súborové I/O — čítanie
```rust
use std::fs;
// Prečítaj celý súbor naraz do String
let obsah: Result<String, std::io::Error> = fs::read_to_string("data.txt");
match obsah {
Ok(text) => println!("Obsah: {}", text),
Err(e) => println!("Chyba: {}", e),
}
// Skrátene s .ok()? v funkcii
fn nacitaj(cesta: &str) -> Option<String> {
fs::read_to_string(cesta).ok()
}
```
### Čítanie po riadkoch
```rust
use std::fs;
fn spracuj_subor(cesta: &str) {
let obsah = fs::read_to_string(cesta).unwrap();
for (i, riadok) in obsah.lines().enumerate() {
println!("{}: {}", i + 1, riadok);
}
}
```
### BufReader — efektívne čítanie po riadkoch
Pre veľké súbory je lepší BufReader — nečíta celý súbor do pamäte:
```rust
use std::fs::File;
use std::io::{BufRead, BufReader};
fn citaj_po_riadkoch(cesta: &str) -> Result<(), std::io::Error> {
let subor = File::open(cesta)?;
let citac = BufReader::new(subor);
for riadok in citac.lines() {
let riadok = riadok?; // každý riadok je Result
println!("{}", riadok);
}
Ok(())
}
```
### Čítanie bajtov
```rust
// Celý súbor ako bajty
let bajty: Vec<u8> = fs::read("obrazok.png").unwrap();
println!("Veľkosť: {} bajtov", bajty.len());
```
### Úlohy 4
**4a.** Napíš funkciu `pocet_riadkov(cesta: &str) -> Option<usize>` — prečíta súbor a vráti počet riadkov.
**4b.** Napíš funkciu `najdi_v_subore(cesta: &str, hladany: &str) -> Vec<String>` — vráti riadky, ktoré obsahujú hľadaný text.
**4c.** Napíš funkciu `nacitaj_cisla(cesta: &str) -> Option<Vec<i32>>` — súbor obsahuje jedno číslo na riadok. Prečítaj, parsuj, vráť Vec.
---
## Kapitola 5: Súborové I/O — zápis
```rust
use std::fs;
// Zapíš celý String do súboru (prepíše ak existuje!)
fs::write("data.txt", "Ahoj svet\nDruhý riadok\n").unwrap();
// Bezpečná verzia v funkcii
fn uloz(cesta: &str, obsah: &str) -> bool {
fs::write(cesta, obsah).is_ok()
}
```
### Pridávanie na koniec (append)
```rust
use std::fs::OpenOptions;
use std::io::Write;
fn pripis(cesta: &str, text: &str) -> bool {
let vysledok = OpenOptions::new()
.create(true) // vytvor ak neexistuje
.append(true) // pridávaj na koniec
.open(cesta);
match vysledok {
Ok(mut subor) => {
writeln!(subor, "{}", text).is_ok()
}
Err(_) => false,
}
}
```
### BufWriter — efektívny zápis
```rust
use std::fs::File;
use std::io::{BufWriter, Write};
fn zapis_vela_riadkov(cesta: &str, riadky: &[String]) -> bool {
let Ok(subor) = File::create(cesta) else {
return false;
};
let mut writer = BufWriter::new(subor);
for riadok in riadky {
if writeln!(writer, "{}", riadok).is_err() {
return false;
}
}
true
}
```
### Úlohy 5
**5a.** Napíš funkciu `uloz_riadky(cesta: &str, riadky: &[String]) -> bool` — ulož vektor riadkov do súboru, každý na nový riadok.
**5b.** Napíš funkciu `pridaj_riadok(cesta: &str, riadok: &str) -> bool` — pridaj riadok na koniec súboru.
**5c.** Napíš program, ktorý číta mená od používateľa a ukladá ich do `"mena.txt"`. Keď zadá "koniec", skončí.
---
## Kapitola 6: PathBuf a Path
`PathBuf` je ako `String` pre cesty k súborom. `Path` je ako `&str`.
```rust
use std::path::{PathBuf, Path};
// Vytváranie PathBuf
let cesta = PathBuf::from("data/subory/kniznica.json");
let cesta: PathBuf = "data.json".into();
// Z &str
let cesta = Path::new("data.json");
// Konverzia
let pathbuf = PathBuf::from("data.json");
let path: &Path = &pathbuf; // PathBuf → &Path
let path: &Path = pathbuf.as_path(); // to isté
// Užitočné metódy
let cesta = PathBuf::from("/home/user/data.json");
cesta.exists(); // existuje súbor?
cesta.is_file(); // je to súbor?
cesta.is_dir(); // je to priečinok?
cesta.extension(); // Some("json")
cesta.file_name(); // Some("data.json")
cesta.parent(); // Some("/home/user")
// Skladanie ciest
let mut cesta = PathBuf::from("/home/user");
cesta.push("dokumenty");
cesta.push("subor.txt");
// /home/user/dokumenty/subor.txt
```
### Na skúške — PathBuf v clape
```rust
use clap::Parser;
use std::path::PathBuf;
#[derive(Parser)]
struct Args {
#[arg(short, long, default_value = "data.json")]
subor: PathBuf,
}
fn main() {
let args = Args::parse();
// args.subor je PathBuf — predávaš priamo do funkcií
let kniznica = Kniznica::nacitaj_zo_suboru(&args.subor)
.unwrap_or_default();
}
```
### Funkcie berúce cestu
```rust
// Berie &PathBuf — funguje s PathBuf
fn nacitaj(cesta: &PathBuf) -> Option<String> {
std::fs::read_to_string(cesta).ok()
}
// Lepšie: berie &Path — funguje s PathBuf aj &str
fn nacitaj(cesta: &Path) -> Option<String> {
std::fs::read_to_string(cesta).ok()
}
// fs::read_to_string berie impl AsRef<Path> — prijme takmer čokoľvek
// Preto funguje:
fs::read_to_string("data.json"); // &str
fs::read_to_string(String::from("data.json")); // String
fs::read_to_string(PathBuf::from("data.json")); // PathBuf
fs::read_to_string(&pathbuf); // &PathBuf
```
### Úlohy 6
**6a.** Vytvor PathBuf z `"/home/user/docs"`. Pridaj `"projekt"` a `"data.json"`. Vypíš celú cestu.
**6b.** Máš PathBuf. Vypíš: existuje? Je súbor? Prípona? Názov súboru? Rodičovský priečinok?
**6c.** Prečo na skúške metóda `nacitaj_zo_suboru` berie `&PathBuf` a nie `&str`?
---
## Kapitola 7: serde JSON I/O — kompletný vzor
Toto je vzor na každej skúške. Kombinácia fs + serde_json.
```rust
use std::fs;
use std::path::PathBuf;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Default)]
struct Kniznica {
knihy: Vec<Kniha>,
}
#[derive(Serialize, Deserialize, Clone)]
struct Kniha {
nazov: String,
autor: String,
}
impl Kniznica {
// ČÍTANIE: súbor → String → štruktúra
fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kniznica> {
// 1. fs::read_to_string — súbor → String
let raw = fs::read_to_string(cesta).ok()?;
// 2. serde_json::from_str — String → štruktúra
serde_json::from_str(&raw).ok()
}
// ZÁPIS: štruktúra → String → súbor
fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
// 1. serde_json::to_string_pretty — štruktúra → String
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
// 2. fs::write — String → súbor
fs::write(cesta, json).is_ok()
}
}
```
Celý tok dát:
```
ČÍTANIE:
súbor na disku
→ fs::read_to_string → String s JSON textom
→ serde_json::from_str → Rust štruktúra
ZÁPIS:
Rust štruktúra
→ serde_json::to_string_pretty → String s JSON textom
→ fs::write → súbor na disku
```
### Použitie v main
```rust
fn main() {
let cesta = PathBuf::from("kniznica.json");
// Načítaj alebo vytvor prázdnu
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
// Urob niečo...
kniznica.knihy.push(Kniha {
nazov: "Duna".into(),
autor: "Herbert".into(),
});
// Ulož
kniznica.uloz_do_suboru(&cesta);
}
```
### Čo sa deje keď súbor neexistuje
```rust
// fs::read_to_string("neexistuje.json")
// → Err(io::Error)
// → .ok() → None
// → ? → vráti None z funkcie
// Preto:
Kniznica::nacitaj_zo_suboru(&PathBuf::from("neexistuje.json"))
// → None
// A v main:
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
// None → unwrap_or_default() → Kniznica::default() → Kniznica { knihy: vec![] }
```
### Úlohy 7
**7a.** Napíš `nacitaj_zo_suboru` a `uloz_do_suboru` pre:
```rust
#[derive(Serialize, Deserialize, Default)]
struct TodoList {
ulohy: Vec<Todo>,
}
#[derive(Serialize, Deserialize, Clone)]
struct Todo {
text: String,
hotova: bool,
}
```
**7b.** Napíš program, ktorý:
1. Načíta TodoList z "todo.json"
2. Pridá novú úlohu
3. Uloží späť
4. Znova načíta a vypíše počet úloh (overenie)
**7c.** Čo sa stane ak `serde_json::to_string_pretty` zlyhá? Kedy sa to stane? (Hint: takmer nikdy pre bežné štruktúry.)
---
## Kapitola 8: IOManager trait — vzor zo skúšky
Na skúške sa I/O abstrahuje cez trait. Toto umožňuje testovanie bez konzoly.
### Prečo nie priamo stdin?
```rust
// PROBLÉM: toto nejde testovať automaticky
fn hraj(hra: &mut Hra) {
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap(); // čaká na konzolu
// ...
}
```
### Riešenie: IOManager trait
```rust
// 1. Definuj trait — AKO získať vstup
pub trait IOManager {
fn ziskaj_pismeno(&mut self) -> char;
}
// 2. Reálna implementácia — čítaj z konzoly
pub struct ConsoleIOManager;
impl IOManager for ConsoleIOManager {
fn ziskaj_pismeno(&mut self) -> char {
let mut vstup = String::new();
std::io::stdin().read_line(&mut vstup).unwrap();
vstup.trim().chars().next().unwrap_or(' ')
}
}
// 3. Metóda berie impl IOManager — funguje s čímkoľvek
impl Hra {
pub fn hraj(&mut self, mut io: impl IOManager) {
while !self.je_koniec_hry() {
println!("Slovo: {}", self.daj_slovo_skryto());
let pismeno = io.ziskaj_pismeno();
let vysledok = self.tipni_pismeno(pismeno);
println!("{}", vysledok);
}
}
}
// 4. Volanie v main
fn main() {
let mut hra = Hra::new("rust");
hra.hraj(ConsoleIOManager); // predáme implementáciu
}
```
### ConsoleIOManager — prázdna štruktúra
```rust
// Prázdna štruktúra — žiadne členy
pub struct ConsoleIOManager;
// Toto nie je typ s poľami. Je to "unit struct".
// Vytváraš ju bez {} :
let io = ConsoleIOManager; // OK
// let io = ConsoleIOManager {}; // tiež OK ale zbytočné
```
### Rozšírený IOManager
Niekedy trait má viac metód:
```rust
pub trait IOManager {
fn ziskaj_pismeno(&mut self) -> char;
fn zobraz_spravu(&self, sprava: &str);
fn zobraz_stav(&self, zivoty: u8, slovo: &str, skusane: &[char]);
}
pub struct ConsoleIOManager;
impl IOManager for ConsoleIOManager {
fn ziskaj_pismeno(&mut self) -> char {
print!("Zadaj písmeno: ");
let mut vstup = String::new();
std::io::stdin().read_line(&mut vstup).unwrap();
vstup.trim().chars().next().unwrap_or(' ')
}
fn zobraz_spravu(&self, sprava: &str) {
println!("{}", sprava);
}
fn zobraz_stav(&self, zivoty: u8, slovo: &str, skusane: &[char]) {
println!("Životov: {}", zivoty);
println!("Slovo: {}", slovo);
println!("Skúšané: {:?}", skusane);
}
}
```
### Úlohy 8
**8a.** Napíš trait `Vstup` s metódou `nacitaj_cislo(&mut self) -> Option<i32>`. Implementuj:
- `KonzolaVstup` — číta z stdin
- `TestovaciVstup { cisla: Vec<i32>, index: usize }` — vracia predpripravené čísla
**8b.** Napíš funkciu `hadaj_cislo(tajne: i32, mut vstup: impl Vstup)` — hra na hádanie čísla. Používa trait namiesto priameho stdin.
**8c.** Máš IOManager z príkladu vyššie. Napíš `TestIOManager`:
```rust
struct TestIOManager {
odpovede: Vec<char>,
index: usize,
}
```
Implementuj IOManager — `ziskaj_pismeno` vráti ďalšie písmeno z `odpovede`.
---
## Kapitola 9: flush — print! bez nového riadku
`print!` nevyprázdni buffer automaticky. Ak chceš aby sa text zobrazil okamžite pred čítaním vstupu:
```rust
use std::io::{self, Write};
fn main() {
print!("Zadaj meno: ");
io::stdout().flush().unwrap(); // DÔLEŽITÉ! Vynúti výpis
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
println!("Ahoj, {}!", vstup.trim());
}
```
Bez `flush()` sa "Zadaj meno: " môže zobraziť až po tom, čo zadáš vstup. S `flush()` sa zobrazí hneď.
```rust
// Kompletný vzor pre prompt
fn prompt(text: &str) -> String {
print!("{}", text);
io::stdout().flush().unwrap();
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
vstup.trim().to_string()
}
// Použitie:
let meno = prompt("Meno: ");
let vek = prompt("Vek: ");
```
### Úlohy 9
**9a.** Napíš funkciu `prompt(text: &str) -> String` s flush. Použi ju na prečítanie mena a veku.
**9b.** Napíš funkciu `prompt_cislo(text: &str) -> Option<i32>` — zobrazí text, prečíta, parsne.
**9c.** Napíš interaktívny program, ktorý používa prompt funkciu na čítanie príkazov v cykle.
---
## Kapitola 10: Prehľad a vzory
### Rýchly prehľad: čo kedy použiť
| Chcem... | Použi |
|----------|-------|
| Čítať celý súbor | `fs::read_to_string(cesta)` |
| Zapísať celý súbor | `fs::write(cesta, obsah)` |
| Čítať po riadkoch (malý súbor) | `fs::read_to_string` + `.lines()` |
| Čítať po riadkoch (veľký súbor) | `BufReader::new(File::open)` + `.lines()` |
| Pripísať na koniec | `OpenOptions::new().append(true)` |
| Čítať z konzoly | `io::stdin().read_line(&mut buf)` |
| Prompt bez newline | `print!()` + `io::stdout().flush()` |
| JSON: súbor → štruktúra | `fs::read_to_string` + `serde_json::from_str` |
| JSON: štruktúra → súbor | `serde_json::to_string_pretty` + `fs::write` |
| Testovateľný I/O | `trait IOManager` + `impl IOManager for ...` |
### Vzor: kompletné menu programu
```rust
use std::io::{self, Write};
use std::path::PathBuf;
fn prompt(text: &str) -> String {
print!("{}", text);
io::stdout().flush().unwrap();
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
vstup.trim().to_string()
}
fn main() {
let cesta = PathBuf::from("data.json");
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
loop {
println!("\n--- KNIŽNICA ---");
println!("1. Pridaj knihu");
println!("2. Vypíš knihy");
println!("3. Hľadaj");
println!("4. Koniec");
let volba = prompt("Vyber: ");
match volba.as_str() {
"1" => {
let nazov = prompt("Názov: ");
let autor = prompt("Autor: ");
// ... pridaj knihu
kniznica.uloz_do_suboru(&cesta);
println!("Pridané.");
}
"2" => {
for kniha in &kniznica.knihy {
println!("{}", kniha);
}
}
"3" => {
let vyraz = prompt("Hľadaj: ");
// ... hľadaj
}
"4" => {
println!("Zbohom!");
break;
}
_ => println!("Neznáma voľba"),
}
}
}
```
### Úlohy 10
**10a.** Napíš kompletný interaktívny program pre TODO list:
- Pridaj úlohu (prompt názov)
- Označ ako hotovú (prompt index)
- Vypíš všetky
- Ulož do JSON
- Koniec
**10b.** Napíš program, ktorý prečíta CSV súbor (meno,vek,mesto), sparsuje riadky do Vec štruktúr, a umožní hľadanie podľa mesta.
**10c.** Implementuj kompletný IOManager trait pre hru Obesenec. Vytvor aj TestIOManager, ktorý automaticky „hrá" hru s predpripravenými písmenami.

674
priprava/rust_priprava14.md Normal file
View File

@@ -0,0 +1,674 @@
# rand — krok za krokom (aktuálna verzia)
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
`Cargo.toml`:
```toml
[dependencies]
rand = "0.10"
```
Najjednoduchší import — dáva ti všetko bežné:
```rust
use rand::prelude::*;
```
---
## Kapitola 1: Generátor — rand::rng()
```rust
use rand::Rng;
fn main() {
let mut rng = rand::rng();
// Náhodné celé číslo (celý rozsah typu)
let cislo: i32 = rng.random();
println!("{}", cislo);
// Náhodné f64 medzi 0.0 a 1.0
let desatinne: f64 = rng.random();
println!("{}", desatinne);
// Náhodné bool
let minca: bool = rng.random();
println!("{}", minca);
}
```
`mut rng` — generátor mení svoj stav pri každom volaní.
### Úlohy 1
**1a.** Vygeneruj 5 náhodných i32 čísel. Vypíš ich.
**1b.** Vygeneruj 10 náhodných bool hodnôt. Spočítaj koľko bolo true.
**1c.** Čo je zle?
```rust
let rng = rand::rng();
let cislo: i32 = rng.random();
```
---
## Kapitola 2: random_range — číslo v rozsahu
```rust
use rand::Rng;
let mut rng = rand::rng();
// Od 1 do 10 (vrátane oboch)
let cislo: i32 = rng.random_range(1..=10);
// Od 1 do 9 (10 NIE JE zahrnuté)
let cislo: i32 = rng.random_range(1..10);
// Float od 0.0 do 1.0 (1.0 nie je zahrnuté)
let cislo: f64 = rng.random_range(0.0..1.0);
// Náhodný index do vektora
let vektor = vec!["a", "b", "c", "d", "e"];
let index: usize = rng.random_range(0..vektor.len());
let nahodny_prvok = &vektor[index];
```
| Zápis | Rozsah | Príklad |
|-------|--------|---------|
| `1..10` | 1 až 9 | bez konca |
| `1..=10` | 1 až 10 | vrátane konca |
| `0..vektor.len()` | 0 až posledný index | náhodný index |
### Úlohy 2
**2a.** Simuluj hod kockou (16). Hoď 100-krát, spočítaj výskyty každého čísla (HashMap).
**2b.** Vygeneruj náhodné heslo — 8 znakov a-z. Hint: `rng.random_range(b'a'..=b'z') as char`.
**2c.** Napíš funkciu `nahodne_cislo(od: i32, do_: i32) -> i32`.
---
## Kapitola 3: choose — náhodný výber z kolekcie
Toto je **najdôležitejšie** na skúške.
```rust
use rand::seq::IndexedRandom; // TOTO TREBA!
let mut rng = rand::rng();
let ovocie = vec!["jablko", "hruška", "banán", "pomaranč"];
// choose — vráti Option (None ak prázdny Vec)
let nahodne = ovocie.choose(&mut rng);
// Some(&"hruška")
match nahodne {
Some(o) => println!("Vybrané: {}", o),
None => println!("Prázdny zoznam"),
}
```
**Import:** `use rand::seq::IndexedRandom;` — bez tohto `choose` nefunguje!
Alebo použi `use rand::prelude::*;` — ten to zahŕňa.
### choose s Vec<String>
```rust
use rand::seq::IndexedRandom;
let slova = vec!["pes".to_string(), "mačka".to_string(), "kôň".to_string()];
let mut rng = rand::rng();
let nahodne: Option<&String> = slova.choose(&mut rng);
// Some(&"mačka")
```
### choose na skúške — SpravcaHry.vytvor_novu_hru
```rust
use rand::seq::IndexedRandom;
use std::collections::HashMap;
struct SpravcaHry {
slovnik: HashMap<String, Vec<String>>,
}
impl SpravcaHry {
fn vytvor_novu_hru(&self, kategoria: &str) -> Option<Hra> {
// 1. Nájdi kategóriu
let slova = self.slovnik.get(kategoria)?;
// 2. Prázdna → None
if slova.is_empty() {
return None;
}
// 3. Vyber náhodné slovo
let mut rng = rand::rng();
let slovo = slova.choose(&mut rng)?;
// 4. Vytvor hru
Some(Hra::new(slovo))
}
}
```
Krok po kroku:
```
slovnik = {"zvierata": ["pes", "mačka", "kôň"]}
kategoria = "zvierata"
→ slovnik.get("zvierata") → Some(&["pes", "mačka", "kôň"])
→ slova.choose(&mut rng) → Some(&"mačka")
→ Hra::new("mačka")
kategoria = "neexistuje"
→ slovnik.get("neexistuje") → None
→ ? → return None
```
### Úlohy 3
**3a.** Máš Vec mien. Vyber náhodné a vypíš.
**3b.** Máš HashMap<String, Vec<String>> (kategória → slová). Napíš funkciu:
```rust
fn nahodne_slovo(slovnik: &HashMap<String, Vec<String>>, kategoria: &str) -> Option<&String>
```
**3c.** Čo sa stane ak zavoláš `.choose()` na prázdnom vektore?
---
## Kapitola 4: sample — viac náhodných prvkov
V novej verzii rand sa `choose_multiple` nahradil metódou `sample`:
```rust
use rand::seq::IndexedRandom;
let cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut rng = rand::rng();
// Vyber 3 náhodné prvky (BEZ opakovania)
let vybrane: Vec<&i32> = cisla.sample(&mut rng, 3).collect();
// napr. [&7, &2, &9]
// Ak chceš viac ako je prvkov, vráti len toľko koľko je
let vsetky: Vec<&i32> = cisla.sample(&mut rng, 100).collect();
```
### Na skúške — výber N otázok
```rust
struct Kviz {
otazky: Vec<Otazka>,
}
impl Kviz {
fn nahodnych_n(&self, n: usize) -> Vec<&Otazka> {
let mut rng = rand::rng();
self.otazky.sample(&mut rng, n).collect()
}
}
```
### Úlohy 4
**4a.** Máš 20 študentov. Vyber 5 náhodných na skúšanie.
**4b.** Máš balíček kariet (Vec<String>). Rozdaj 7 kariet.
---
## Kapitola 5: shuffle — zamiešanie
```rust
use rand::seq::SliceRandom; // shuffle je v SliceRandom!
let mut cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut rng = rand::rng();
// shuffle — zamieša NA MIESTE (mení Vec)
cisla.shuffle(&mut rng);
// napr. [7, 2, 9, 1, 5, 3, 10, 4, 8, 6]
```
**Import:** `use rand::seq::SliceRandom;` — shuffle je v SliceRandom, nie IndexedRandom!
### Kedy čo
| Chcem... | Použi | Import |
|----------|-------|--------|
| 1 náhodný prvok | `.choose()` | `IndexedRandom` |
| N náhodných (bez zmeny originálu) | `.sample()` | `IndexedRandom` |
| Zamiešať celý zoznam | `.shuffle()` | `SliceRandom` |
### Na skúške — zamiešanie otázok
```rust
use rand::seq::SliceRandom;
impl Kviz {
fn hraj(&mut self) {
let mut rng = rand::rng();
self.otazky.shuffle(&mut rng);
for otazka in &self.otazky {
println!("{}", otazka.text);
}
}
}
```
### Úlohy 5
**5a.** Máš vektor kariet. Zamiešaj. Vypíš prvých 5.
**5b.** Čo je zle?
```rust
let cisla = vec![1, 2, 3, 4, 5];
cisla.shuffle(&mut rand::rng());
```
---
## Kapitola 6: Náhodné znaky a reťazce
```rust
use rand::Rng;
use rand::distr::Alphanumeric; // distr, nie distributions!
let mut rng = rand::rng();
// Náhodný ASCII znak a-z
let znak: char = rng.random_range(b'a'..=b'z') as char;
// Náhodný veľký znak A-Z
let znak: char = rng.random_range(b'A'..=b'Z') as char;
// Alfanumerický reťazec (a-z, A-Z, 0-9)
let heslo: String = (0..12)
.map(|_| rng.sample(Alphanumeric) as char)
.collect();
// napr. "a7Bk9xQ2mF1p"
```
### Výber náhodného znaku z reťazca
```rust
use rand::seq::IndexedRandom;
let abeceda: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
let mut rng = rand::rng();
let nahodny_znak = abeceda.choose(&mut rng);
// Some(&'m')
```
### Úlohy 6
**6a.** Vygeneruj heslo: 10 znakov, len malé písmená a-z.
**6b.** Vygeneruj náhodné meno: veľké prvé + 4 malé písmená.
---
## Kapitola 7: random_bool — pravdepodobnosť
```rust
use rand::Rng;
let mut rng = rand::rng();
// 50% šanca na true
let minca: bool = rng.random_bool(0.5);
// 70% šanca na true
let dost_casto: bool = rng.random_bool(0.7);
// 10% šanca na true
let zriedka: bool = rng.random_bool(0.1);
```
### Úlohy 7
**7a.** Simuluj 1000 hodov mincou. Koľkokrát padol orol (true)?
**7b.** Funkcia vráti "Výhra!" s 15% pravdepodobnosťou, inak "Skús znova".
---
## Kapitola 8: Kombinácia s IO — interaktívne hry
### Hádaj číslo
```rust
use rand::Rng;
use std::io::{self, Write};
fn prompt(text: &str) -> String {
print!("{}", text);
io::stdout().flush().unwrap();
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
vstup.trim().to_string()
}
fn main() {
let mut rng = rand::rng();
let tajne: i32 = rng.random_range(1..=100);
let mut pokusy = 0;
println!("Hádaj číslo od 1 do 100!");
loop {
let vstup = prompt("Tvoj tip: ");
let tip: i32 = match vstup.parse() {
Ok(n) => n,
Err(_) => {
println!("Zadaj číslo!");
continue;
}
};
pokusy += 1;
if tip < tajne {
println!("Viac!");
} else if tip > tajne {
println!("Menej!");
} else {
println!("Správne! Uhádol si na {} pokusov.", pokusy);
break;
}
}
}
```
### Obesenec s náhodným slovom
```rust
use rand::seq::IndexedRandom;
use std::collections::{HashSet, HashMap};
use std::io::{self, Write};
fn nacitaj_znak() -> Option<char> {
print!("Zadaj písmeno: ");
io::stdout().flush().unwrap();
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).ok()?;
vstup.trim().to_lowercase().chars().next()
}
fn main() {
let slovnik: HashMap<&str, Vec<&str>> = HashMap::from([
("zvierata", vec!["pes", "macka", "kon", "krava", "zajac"]),
("jedlo", vec!["pizza", "burger", "salat", "rizoto"]),
]);
// Vyber náhodnú kategóriu a slovo
let mut rng = rand::rng();
let kategorie: Vec<&&str> = slovnik.keys().collect();
let kategoria = **kategorie.choose(&mut rng).unwrap();
let slova = &slovnik[kategoria];
let slovo = *slova.choose(&mut rng).unwrap();
println!("Kategória: {}", kategoria);
let mut uhadnute: HashSet<char> = HashSet::new();
let mut skusane: HashSet<char> = HashSet::new();
let mut zivoty: u8 = 6;
loop {
let zobrazene: String = slovo.chars().map(|c| {
if uhadnute.contains(&c) { c } else { '_' }
}).collect();
println!("\n{} (životov: {})", zobrazene, zivoty);
if slovo.chars().all(|c| uhadnute.contains(&c)) {
println!("Vyhral si! Slovo bolo: {}", slovo);
break;
}
if zivoty == 0 {
println!("Prehral si! Slovo bolo: {}", slovo);
break;
}
let Some(pismeno) = nacitaj_znak() else {
println!("Neplatný vstup");
continue;
};
if !skusane.insert(pismeno) {
println!("Už si skúšal!");
continue;
}
if slovo.contains(pismeno) {
uhadnute.insert(pismeno);
println!("Správne!");
} else {
zivoty -= 1;
println!("Zle!");
}
}
}
```
### Úlohy 8
**8a.** Napíš "hádaj číslo" s obtiažnosťou: ľahká (110), stredná (150), ťažká (1100).
**8b.** Napíš kvízovú hru: 5 otázok z náhodne zamiešaného poolu, hráč zadáva číslo odpovede.
---
## Kapitola 9: Náhodné z HashMap a HashSet
Pre iterátory (nie slice) treba `IteratorRandom`:
```rust
use rand::seq::IteratorRandom; // pre .choose() na iterátore
let mut mapa = HashMap::new();
mapa.insert("a", 1);
mapa.insert("b", 2);
mapa.insert("c", 3);
let mut rng = rand::rng();
// Náhodný kľúč
let kluc = mapa.keys().choose(&mut rng);
// Some(&"b")
// Náhodný pár
let par = mapa.iter().choose(&mut rng);
// Some((&"a", &1))
```
```rust
// HashSet — rovnako
use rand::seq::IteratorRandom;
let mnozina: HashSet<String> = vec!["pes", "mačka", "kôň"]
.into_iter().map(String::from).collect();
let mut rng = rand::rng();
let nahodny = mnozina.iter().choose(&mut rng);
// Some(&"mačka")
```
### Úlohy 9
**9a.** Máš HashMap kategórií. Vyber náhodnú kategóriu, potom náhodné slovo z nej.
**9b.** Máš HashSet<char> skúšaných písmen. Vyber písmeno z abecedy, ktoré ešte NIE JE v sete.
---
## Kapitola 10: Kompletný vzor — SpravcaHry zo skúšky
```rust
use rand::seq::IndexedRandom;
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Default)]
pub struct SpravcaHry {
pub slovnik: HashMap<String, Vec<String>>,
}
impl SpravcaHry {
pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<SpravcaHry> {
let raw = std::fs::read_to_string(cesta).ok()?;
serde_json::from_str(&raw).ok()
}
pub fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
std::fs::write(cesta, json).is_ok()
}
pub fn vytvor_novu_hru(&self, kategoria: &str) -> Option<Hra> {
let slova = self.slovnik.get(kategoria)?;
if slova.is_empty() {
return None;
}
let mut rng = rand::rng();
let slovo = slova.choose(&mut rng)?;
Some(Hra::new(slovo))
}
pub fn pridaj_kategoriu(&mut self, kategoria: &str) -> Result<(), ()> {
if self.slovnik.contains_key(kategoria) {
return Err(());
}
self.slovnik.insert(kategoria.to_string(), Vec::new());
Ok(())
}
pub fn pridaj_slovo(&mut self, kategoria: &str, slovo: &str) -> Result<(), ()> {
let slova = self.slovnik.get_mut(kategoria).ok_or(())?;
slova.push(slovo.to_string());
Ok(())
}
}
```
### main.rs s clapom
```rust
use clap::{Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
struct Args {
#[command(subcommand)]
cmd: Cmd,
}
#[derive(Subcommand)]
enum Cmd {
PridajKategoriu {
cesta: PathBuf,
kategoria: String,
},
PridajSlovo {
cesta: PathBuf,
kategoria: String,
slovo: String,
},
Hraj {
cesta: PathBuf,
kategoria: String,
},
}
fn main() {
let args = Args::parse();
match args.cmd {
Cmd::PridajKategoriu { cesta, kategoria } => {
let mut spravca = SpravcaHry::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
match spravca.pridaj_kategoriu(&kategoria) {
Ok(()) => {
spravca.uloz_do_suboru(&cesta);
println!("Kategória pridaná.");
}
Err(()) => println!("Kategória už existuje."),
}
}
Cmd::PridajSlovo { cesta, kategoria, slovo } => {
let mut spravca = SpravcaHry::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
match spravca.pridaj_slovo(&kategoria, &slovo) {
Ok(()) => {
spravca.uloz_do_suboru(&cesta);
println!("Slovo pridané.");
}
Err(()) => println!("Kategória neexistuje."),
}
}
Cmd::Hraj { cesta, kategoria } => {
let spravca = SpravcaHry::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
match spravca.vytvor_novu_hru(&kategoria) {
Some(mut hra) => {
hra.hraj(ConsoleIOManager);
}
None => println!("Kategória neexistuje alebo je prázdna."),
}
}
}
}
```
### Úlohy 10
**10a.** Napíš celý SpravcaHry od nuly bez pozerania.
**10b.** Napíš main.rs s clapom bez pozerania.
**10c.** Rozšír o metódu `nahodna_kategoria(&self) -> Option<&String>`. Použi `IteratorRandom`.
---
## Prehľad: čo importovať a kedy
| Chcem... | Import | Metóda |
|----------|--------|--------|
| Náhodné číslo | `use rand::Rng;` | `rng.random()` |
| Číslo v rozsahu | `use rand::Rng;` | `rng.random_range(1..=6)` |
| Náhodný bool | `use rand::Rng;` | `rng.random_bool(0.5)` |
| Náhodný prvok z Vec | `use rand::seq::IndexedRandom;` | `vec.choose(&mut rng)` |
| N náhodných prvkov | `use rand::seq::IndexedRandom;` | `vec.sample(&mut rng, n)` |
| Zamiešať Vec | `use rand::seq::SliceRandom;` | `vec.shuffle(&mut rng)` |
| Náhodný z iterátora | `use rand::seq::IteratorRandom;` | `iter.choose(&mut rng)` |
| Alfanumerické znaky | `use rand::distr::Alphanumeric;` | `rng.sample(Alphanumeric) as char` |
| Všetko naraz | `use rand::prelude::*;` | zahŕňa Rng, IndexedRandom, SliceRandom |
**Vždy:** `let mut rng = rand::rng();`
## Zmeny oproti starým verziám (0.8)
Ak niekde vidíš starý kód, tu je preklad:
| Staré (rand 0.8) | Nové (rand 0.9+) |
|-------------------|-------------------|
| `rand::thread_rng()` | `rand::rng()` |
| `rng.gen()` | `rng.random()` |
| `rng.gen_range(1..=6)` | `rng.random_range(1..=6)` |
| `rng.gen_bool(0.5)` | `rng.random_bool(0.5)` |
| `use rand::distributions::` | `use rand::distr::` |
| `vec.choose_multiple(&mut rng, n)` | `vec.sample(&mut rng, n)` |
| `use rand::seq::SliceRandom;` (pre choose) | `use rand::seq::IndexedRandom;` |

298
priprava/rust_priprava15.md Normal file
View File

@@ -0,0 +1,298 @@
# Ok, ok(), is_ok(), () — rýchlokurz
---
## 1. Dva svety: Result a Option
```
Result<T, E> → Ok(hodnota) alebo Err(chyba)
Option<T> → Some(hodnota) alebo None
```
Result používaš keď chceš vedieť ČO sa pokazilo. Option keď ťa zaujíma len či niečo existuje alebo nie.
---
## 2. Čo je ()
`()` je "unit type" — prázdna hodnota. Znamená "nič zaujímavé".
```rust
// Funkcia, ktorá nič nevracia, v skutočnosti vracia ()
fn pozdrav() {
println!("ahoj");
}
// je to isté ako:
fn pozdrav() -> () {
println!("ahoj");
}
```
---
## 3. Ok(()) — úspech bez hodnoty
Keď metóda buď uspeje (ale nemá čo vrátiť) alebo zlyhá:
```rust
fn pridaj_knihu(&mut self, kniha: Kniha) -> Result<(), ()> {
if self.knihy.iter().any(|k| k.nazov == kniha.nazov) {
return Err(()); // zlyhalo — duplikát
}
self.knihy.push(kniha);
Ok(()) // úspech — ale nemám čo vrátiť, tak ()
}
```
`Ok(())` = "podarilo sa, nemám ti čo dať".
`Err(())` = "nepodarilo sa, a ani ti nepoviem prečo".
### Kedy Result<(), ()>
Keď nepotrebuješ ani návratovú hodnotu ani detail chyby:
```rust
fn pridaj(&mut self, meno: &str) -> Result<(), ()> { ... }
fn nastav(&mut self, hodnota: u32) -> Result<(), ()> { ... }
```
### Kedy Result<T, ()>
Keď pri úspechu vraciaš niečo, ale chyba nemá detail:
```rust
fn odstran(&mut self, meno: &str) -> Result<Kniha, ()> { ... }
// Ok(kniha) — tu je odstránená kniha
// Err(()) — nenašiel som ju
```
### Kedy Result<T, String>
Keď chceš popísať chybu:
```rust
fn pridaj(&mut self, kniha: Kniha) -> Result<(), String> {
if self.knihy.iter().any(|k| k.nazov == kniha.nazov) {
return Err(format!("Kniha '{}' už existuje", kniha.nazov));
}
self.knihy.push(kniha);
Ok(())
}
```
---
## 4. .ok() — Result → Option (zahoď chybu)
`.ok()` premení Result na Option. Ak bol Err, zahodí chybu a dá None:
```rust
Result<T, E> .ok() Option<T>
Ok(42) .ok() Some(42)
Err("chyba") .ok() None
```
```rust
// Prakticky:
let cislo: Result<i32, _> = "42".parse();
let cislo: Option<i32> = "42".parse().ok(); // Some(42)
let cislo: Option<i32> = "abc".parse().ok(); // None
// Súborový I/O:
let obsah: Result<String, io::Error> = fs::read_to_string("data.json");
let obsah: Option<String> = fs::read_to_string("data.json").ok();
```
**Kedy:** Keď ťa nezaujíma detail chyby, len či sa podarilo.
---
## 5. .ok()? — najčastejší vzor na skúške
`.ok()` + `?` spolu — premení Result na Option a ak je None, vráti None z funkcie:
```rust
fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kniznica> {
let raw = fs::read_to_string(cesta).ok()?;
// ^^^^^^^^^^^^^^^^^^^^^^^^^ Result<String, Error>
// .ok() → Option<String>
// ? → ak None, return None; ak Some, rozbaľ
serde_json::from_str(&raw).ok()
// ^^^ posledný riadok — .ok() premení na Option, vráti sa
}
```
Rozklad:
```
Súbor existuje:
fs::read_to_string → Ok("{ ... }")
.ok() → Some("{ ... }")
? → "{ ... }" (rozbalí Some)
serde_json::from_str → Ok(Kniznica{...})
.ok() → Some(Kniznica{...})
→ funkcia vráti Some(Kniznica{...})
Súbor neexistuje:
fs::read_to_string → Err(io::Error)
.ok() → None
? → return None (okamžite opustí funkciu)
```
---
## 6. .is_ok() a .is_err() — bool kontrola
```rust
let vysledok: Result<i32, String> = Ok(42);
vysledok.is_ok() // true
vysledok.is_err() // false
let vysledok: Result<i32, String> = Err("chyba".into());
vysledok.is_ok() // false
vysledok.is_err() // true
```
**Hlavné použitie — ukladanie:**
```rust
fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
fs::write(cesta, json).is_ok()
// ^^^^^^ Result → bool
// úspech → true, chyba → false
}
```
`fs::write` vracia `Result<(), Error>`. Nás zaujíma len "podarilo sa?" → `.is_ok()``bool`.
---
## 7. .ok_or() a .ok_or_else() — Option → Result
Opačný smer: máš Option, chceš Result.
```rust
Option<T> .ok_or(chyba) Result<T, E>
Some(42) .ok_or("nič") Ok(42)
None .ok_or("nič") Err("nič")
```
```rust
// Prakticky — position vracia Option, ty chceš Result
fn odstran(&mut self, meno: &str) -> Result<String, ()> {
let pos = self.mena.iter().position(|m| m == meno).ok_or(())?;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Option<usize>
// .ok_or(()) → Result<usize, ()>
// ? → ak Err, return Err
Ok(self.mena.remove(pos))
}
```
```rust
// ok_or_else — lazy verzia (closure sa zavolá len ak None)
fn najdi(&self, meno: &str) -> Result<&Kniha, String> {
self.knihy.iter()
.find(|k| k.nazov == meno)
.ok_or_else(|| format!("Kniha '{}' nenájdená", meno))
}
```
---
## 8. .unwrap_or() a .unwrap_or_default() — fallback hodnota
```rust
// Option
let cislo: Option<i32> = None;
cislo.unwrap_or(0) // 0 — fallback
cislo.unwrap_or_default() // 0 — default pre i32
// Result
let cislo: Result<i32, _> = "abc".parse();
cislo.unwrap_or(0) // 0 — fallback
cislo.unwrap_or_default() // 0
// Na skúške — načítanie s fallbackom
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta) // Option<Kniznica>
.unwrap_or_default(); // Kniznica::default() ak None
```
---
## 9. let...else — rozbaľ alebo odíď
```rust
// Ak je Ok, rozbaľ do premennej. Ak Err, vykonaj blok (musí divergovať — return/break/continue).
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
// tu json je String
// Ak je Some, rozbaľ. Ak None, odíď.
let Some(kniha) = self.knihy.iter().find(|k| k.nazov == nazov) else {
return None;
};
// tu kniha je &Kniha
```
---
## 10. Veľký prehľad: čo kedy
### Mám Result, chcem...
| Chcem | Použi | Príklad |
|-------|-------|---------|
| Option (zahodiť chybu) | `.ok()` | `"42".parse().ok()``Some(42)` |
| bool (podarilo sa?) | `.is_ok()` | `fs::write(c, d).is_ok()``true` |
| Rozbaľ alebo vráť None | `.ok()?` | `fs::read_to_string(c).ok()?` |
| Rozbaľ alebo panic | `.unwrap()` | len v testoch! |
| Rozbaľ alebo fallback | `.unwrap_or(val)` | `vstup.parse().unwrap_or(0)` |
| Rozbaľ alebo default | `.unwrap_or_default()` | `nacitaj().unwrap_or_default()` |
| Rozbaľ alebo return | `let Ok(x) = ... else { return }` | serializácia v uloz |
### Mám Option, chcem...
| Chcem | Použi | Príklad |
|-------|-------|---------|
| Result (pridať chybu) | `.ok_or(err)` | `vec.find(x).ok_or(())` |
| Rozbaľ alebo vráť None | `?` | `slovnik.get(kat)?` |
| bool (existuje?) | `.is_some()` / `.is_none()` | `mapa.get("x").is_some()` |
| Rozbaľ alebo fallback | `.unwrap_or(val)` | `mapa.get("x").unwrap_or(&0)` |
| Rozbaľ alebo default | `.unwrap_or_default()` | `nacitaj().unwrap_or_default()` |
| Rozbaľ alebo return | `let Some(x) = ... else { return }` | |
### Chcem vrátiť z funkcie...
| Návratový typ | Úspech | Zlyhanie |
|---------------|--------|----------|
| `Result<(), ()>` | `Ok(())` | `Err(())` |
| `Result<T, ()>` | `Ok(hodnota)` | `Err(())` |
| `Result<(), String>` | `Ok(())` | `Err("popis".into())` |
| `Result<T, String>` | `Ok(hodnota)` | `Err(format!("..."))` |
| `Option<T>` | `Some(hodnota)` | `None` |
| `bool` | `true` | `false` |
### Konverzie — kompletná mapa
```
Result<T, E> ──.ok()──────→ Option<T> (zahodí E)
Result<T, E> ──.is_ok()───→ bool
Result<T, E> ──.is_err()──→ bool
Option<T> ──.ok_or(e)─────→ Result<T, E> (pridá E)
Option<T> ──.is_some()────→ bool
Option<T> ──.is_none()────→ bool
Result<T, E> ──.unwrap_or(val)────→ T
Option<T> ──.unwrap_or(val)────→ T
Result<T, E> ──.unwrap_or_default()──→ T (T: Default)
Option<T> ──.unwrap_or_default()──→ T (T: Default)
```

298
priprava/rust_priprava16.md Normal file
View File

@@ -0,0 +1,298 @@
# Ok, ok(), is_ok(), () — rýchlokurz
---
## 1. Dva svety: Result a Option
```
Result<T, E> → Ok(hodnota) alebo Err(chyba)
Option<T> → Some(hodnota) alebo None
```
Result používaš keď chceš vedieť ČO sa pokazilo. Option keď ťa zaujíma len či niečo existuje alebo nie.
---
## 2. Čo je ()
`()` je "unit type" — prázdna hodnota. Znamená "nič zaujímavé".
```rust
// Funkcia, ktorá nič nevracia, v skutočnosti vracia ()
fn pozdrav() {
println!("ahoj");
}
// je to isté ako:
fn pozdrav() -> () {
println!("ahoj");
}
```
---
## 3. Ok(()) — úspech bez hodnoty
Keď metóda buď uspeje (ale nemá čo vrátiť) alebo zlyhá:
```rust
fn pridaj_knihu(&mut self, kniha: Kniha) -> Result<(), ()> {
if self.knihy.iter().any(|k| k.nazov == kniha.nazov) {
return Err(()); // zlyhalo — duplikát
}
self.knihy.push(kniha);
Ok(()) // úspech — ale nemám čo vrátiť, tak ()
}
```
`Ok(())` = "podarilo sa, nemám ti čo dať".
`Err(())` = "nepodarilo sa, a ani ti nepoviem prečo".
### Kedy Result<(), ()>
Keď nepotrebuješ ani návratovú hodnotu ani detail chyby:
```rust
fn pridaj(&mut self, meno: &str) -> Result<(), ()> { ... }
fn nastav(&mut self, hodnota: u32) -> Result<(), ()> { ... }
```
### Kedy Result<T, ()>
Keď pri úspechu vraciaš niečo, ale chyba nemá detail:
```rust
fn odstran(&mut self, meno: &str) -> Result<Kniha, ()> { ... }
// Ok(kniha) — tu je odstránená kniha
// Err(()) — nenašiel som ju
```
### Kedy Result<T, String>
Keď chceš popísať chybu:
```rust
fn pridaj(&mut self, kniha: Kniha) -> Result<(), String> {
if self.knihy.iter().any(|k| k.nazov == kniha.nazov) {
return Err(format!("Kniha '{}' už existuje", kniha.nazov));
}
self.knihy.push(kniha);
Ok(())
}
```
---
## 4. .ok() — Result → Option (zahoď chybu)
`.ok()` premení Result na Option. Ak bol Err, zahodí chybu a dá None:
```rust
Result<T, E> .ok() Option<T>
Ok(42) .ok() Some(42)
Err("chyba") .ok() None
```
```rust
// Prakticky:
let cislo: Result<i32, _> = "42".parse();
let cislo: Option<i32> = "42".parse().ok(); // Some(42)
let cislo: Option<i32> = "abc".parse().ok(); // None
// Súborový I/O:
let obsah: Result<String, io::Error> = fs::read_to_string("data.json");
let obsah: Option<String> = fs::read_to_string("data.json").ok();
```
**Kedy:** Keď ťa nezaujíma detail chyby, len či sa podarilo.
---
## 5. .ok()? — najčastejší vzor na skúške
`.ok()` + `?` spolu — premení Result na Option a ak je None, vráti None z funkcie:
```rust
fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kniznica> {
let raw = fs::read_to_string(cesta).ok()?;
// ^^^^^^^^^^^^^^^^^^^^^^^^^ Result<String, Error>
// .ok() → Option<String>
// ? → ak None, return None; ak Some, rozbaľ
serde_json::from_str(&raw).ok()
// ^^^ posledný riadok — .ok() premení na Option, vráti sa
}
```
Rozklad:
```
Súbor existuje:
fs::read_to_string → Ok("{ ... }")
.ok() → Some("{ ... }")
? → "{ ... }" (rozbalí Some)
serde_json::from_str → Ok(Kniznica{...})
.ok() → Some(Kniznica{...})
→ funkcia vráti Some(Kniznica{...})
Súbor neexistuje:
fs::read_to_string → Err(io::Error)
.ok() → None
? → return None (okamžite opustí funkciu)
```
---
## 6. .is_ok() a .is_err() — bool kontrola
```rust
let vysledok: Result<i32, String> = Ok(42);
vysledok.is_ok() // true
vysledok.is_err() // false
let vysledok: Result<i32, String> = Err("chyba".into());
vysledok.is_ok() // false
vysledok.is_err() // true
```
**Hlavné použitie — ukladanie:**
```rust
fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
fs::write(cesta, json).is_ok()
// ^^^^^^ Result → bool
// úspech → true, chyba → false
}
```
`fs::write` vracia `Result<(), Error>`. Nás zaujíma len "podarilo sa?" → `.is_ok()``bool`.
---
## 7. .ok_or() a .ok_or_else() — Option → Result
Opačný smer: máš Option, chceš Result.
```rust
Option<T> .ok_or(chyba) Result<T, E>
Some(42) .ok_or("nič") Ok(42)
None .ok_or("nič") Err("nič")
```
```rust
// Prakticky — position vracia Option, ty chceš Result
fn odstran(&mut self, meno: &str) -> Result<String, ()> {
let pos = self.mena.iter().position(|m| m == meno).ok_or(())?;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Option<usize>
// .ok_or(()) → Result<usize, ()>
// ? → ak Err, return Err
Ok(self.mena.remove(pos))
}
```
```rust
// ok_or_else — lazy verzia (closure sa zavolá len ak None)
fn najdi(&self, meno: &str) -> Result<&Kniha, String> {
self.knihy.iter()
.find(|k| k.nazov == meno)
.ok_or_else(|| format!("Kniha '{}' nenájdená", meno))
}
```
---
## 8. .unwrap_or() a .unwrap_or_default() — fallback hodnota
```rust
// Option
let cislo: Option<i32> = None;
cislo.unwrap_or(0) // 0 — fallback
cislo.unwrap_or_default() // 0 — default pre i32
// Result
let cislo: Result<i32, _> = "abc".parse();
cislo.unwrap_or(0) // 0 — fallback
cislo.unwrap_or_default() // 0
// Na skúške — načítanie s fallbackom
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta) // Option<Kniznica>
.unwrap_or_default(); // Kniznica::default() ak None
```
---
## 9. let...else — rozbaľ alebo odíď
```rust
// Ak je Ok, rozbaľ do premennej. Ak Err, vykonaj blok (musí divergovať — return/break/continue).
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
// tu json je String
// Ak je Some, rozbaľ. Ak None, odíď.
let Some(kniha) = self.knihy.iter().find(|k| k.nazov == nazov) else {
return None;
};
// tu kniha je &Kniha
```
---
## 10. Veľký prehľad: čo kedy
### Mám Result, chcem...
| Chcem | Použi | Príklad |
|-------|-------|---------|
| Option (zahodiť chybu) | `.ok()` | `"42".parse().ok()``Some(42)` |
| bool (podarilo sa?) | `.is_ok()` | `fs::write(c, d).is_ok()``true` |
| Rozbaľ alebo vráť None | `.ok()?` | `fs::read_to_string(c).ok()?` |
| Rozbaľ alebo panic | `.unwrap()` | len v testoch! |
| Rozbaľ alebo fallback | `.unwrap_or(val)` | `vstup.parse().unwrap_or(0)` |
| Rozbaľ alebo default | `.unwrap_or_default()` | `nacitaj().unwrap_or_default()` |
| Rozbaľ alebo return | `let Ok(x) = ... else { return }` | serializácia v uloz |
### Mám Option, chcem...
| Chcem | Použi | Príklad |
|-------|-------|---------|
| Result (pridať chybu) | `.ok_or(err)` | `vec.find(x).ok_or(())` |
| Rozbaľ alebo vráť None | `?` | `slovnik.get(kat)?` |
| bool (existuje?) | `.is_some()` / `.is_none()` | `mapa.get("x").is_some()` |
| Rozbaľ alebo fallback | `.unwrap_or(val)` | `mapa.get("x").unwrap_or(&0)` |
| Rozbaľ alebo default | `.unwrap_or_default()` | `nacitaj().unwrap_or_default()` |
| Rozbaľ alebo return | `let Some(x) = ... else { return }` | |
### Chcem vrátiť z funkcie...
| Návratový typ | Úspech | Zlyhanie |
|---------------|--------|----------|
| `Result<(), ()>` | `Ok(())` | `Err(())` |
| `Result<T, ()>` | `Ok(hodnota)` | `Err(())` |
| `Result<(), String>` | `Ok(())` | `Err("popis".into())` |
| `Result<T, String>` | `Ok(hodnota)` | `Err(format!("..."))` |
| `Option<T>` | `Some(hodnota)` | `None` |
| `bool` | `true` | `false` |
### Konverzie — kompletná mapa
```
Result<T, E> ──.ok()──────→ Option<T> (zahodí E)
Result<T, E> ──.is_ok()───→ bool
Result<T, E> ──.is_err()──→ bool
Option<T> ──.ok_or(e)─────→ Result<T, E> (pridá E)
Option<T> ──.is_some()────→ bool
Option<T> ──.is_none()────→ bool
Result<T, E> ──.unwrap_or(val)────→ T
Option<T> ──.unwrap_or(val)────→ T
Result<T, E> ──.unwrap_or_default()──→ T (T: Default)
Option<T> ──.unwrap_or_default()──→ T (T: Default)
```

1119
priprava/rust_priprava3.md Normal file

File diff suppressed because it is too large Load Diff

762
priprava/rust_priprava4.md Normal file
View File

@@ -0,0 +1,762 @@
# HashMap — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
Riešenia sú na konci, číslované.
```rust
use std::collections::HashMap; // toto treba vždy
```
---
## Kapitola 1: Vytvorenie a insert
HashMap je kolekcia párov kľúč → hodnota. Kľúč je unikátny.
```rust
let mut mapa: HashMap<String, i32> = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// mapa = {"Anna": 25, "Boris": 30}
// insert na existujúci kľúč PREPÍŠE hodnotu a vráti starú
let stara = mapa.insert("Anna".to_string(), 26);
// stara = Some(25), mapa = {"Anna": 26, "Boris": 30}
// insert na nový kľúč vráti None
let stara = mapa.insert("Cyril".to_string(), 22);
// stara = None
```
### Úlohy 1
**1a.** Vytvor HashMap kde kľúče sú mená ovocia (String) a hodnoty sú ceny (f64). Vlož 3 ovocia.
**1b.** Čo vráti posledný insert? Napíš kód a over:
```rust
let mut m = HashMap::new();
m.insert("x", 1);
m.insert("y", 2);
let vysledok = m.insert("x", 99);
println!("{:?}", vysledok);
```
**1c.** Vytvor HashMap z vektora dvojíc pomocou `.collect()`:
```rust
let data = vec![("sk", "Slovensko"), ("cz", "Česko"), ("pl", "Poľsko")];
// tvoj kód → HashMap<&str, &str>
```
---
## Kapitola 2: Čítanie — get, contains_key, len
```rust
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// get — vráti Option<&V>
let vek = mapa.get("Anna"); // Some(&25)
let vek = mapa.get("Cyril"); // None
// contains_key — vráti bool
let je_tam = mapa.contains_key("Anna"); // true
let je_tam = mapa.contains_key("Cyril"); // false
// len — počet párov
let pocet = mapa.len(); // 2
// is_empty
let prazdna = mapa.is_empty(); // false
```
**Dôležité:** Nikdy nepoužívaj `mapa["Anna"]` — ak kľúč neexistuje, program spadne. Vždy `.get()`.
### Úlohy 2
**2a.** Máš mapu ovocia z úlohy 1a. Napíš kód, ktorý:
- Vypíše cenu jablka ak existuje
- Vypíše "Nemáme" ak neexistuje
Použi `if let` s `.get()`.
**2b.** Napíš funkciu:
```rust
fn je_v_mape(mapa: &HashMap<String, i32>, kluc: &str) -> bool
```
Použi `contains_key`.
**2c.** Čo vypíše? Tipni, potom over:
```rust
let mut m: HashMap<&str, i32> = HashMap::new();
m.insert("a", 1);
println!("{:?}", m.get("a"));
println!("{:?}", m.get("b"));
println!("{}", m.len());
println!("{}", m.contains_key("a"));
println!("{}", m.contains_key("b"));
```
---
## Kapitola 3: Mazanie — remove, clear
```rust
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// remove — vráti Option<V> (hodnotu, nie referenciu)
let odstraneny = mapa.remove("Anna");
// odstraneny = Some(25), mapa = {"Boris": 30}
let odstraneny = mapa.remove("Cyril");
// odstraneny = None (neexistoval)
// clear — vymaže všetko
mapa.clear();
// mapa = {}
```
### Úlohy 3
**3a.** Vytvor mapu s 3 položkami. Odstráň jednu. Vypíš čo `remove` vrátil. Vypíš veľkosť mapy.
**3b.** Napíš funkciu:
```rust
fn odstran_ak_existuje(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32>
```
Ak kľúč existuje, odstráň ho a vráť hodnotu. Ak nie, vráť None. (Hint: `remove` presne toto robí.)
---
## Kapitola 4: Iterácia
```rust
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
mapa.insert("Cyril".to_string(), 22);
// Cez páry — poradie NIE JE garantované!
for (meno, vek) in &mapa {
println!("{}: {}", meno, vek);
}
// Len kľúče
for meno in mapa.keys() {
println!("{}", meno);
}
// Len hodnoty
for vek in mapa.values() {
println!("{}", vek);
}
// Meniteľné hodnoty
for vek in mapa.values_mut() {
*vek += 1;
}
```
### Úlohy 4
**4a.** Vytvor mapu produktov (názov → cena). Vypíš všetky produkty vo formáte `"Produkt: cena €"`.
**4b.** Napíš funkciu, ktorá vráti vektor všetkých kľúčov z mapy:
```rust
fn kluce(mapa: &HashMap<String, i32>) -> Vec<&String>
```
**4c.** Napíš funkciu, ktorá zvýši všetky hodnoty v mape o 10:
```rust
fn zvys_vsetky(mapa: &mut HashMap<String, i32>)
```
**4d.** Napíš funkciu, ktorá vráti súčet všetkých hodnôt:
```rust
fn sucet_hodnot(mapa: &HashMap<String, i32>) -> i32
```
Použi `.values()` a `.sum()`.
---
## Kapitola 5: get_mut — zmena hodnoty na mieste
Ak chceš zmeniť jednu konkrétnu hodnotu (nie všetky), použi `get_mut`:
```rust
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
// get_mut vráti Option<&mut V>
if let Some(vek) = mapa.get_mut("Anna") {
*vek += 1; // Anna má teraz 26
}
// Ak kľúč neexistuje, get_mut vráti None
if let Some(vek) = mapa.get_mut("Boris") {
*vek += 1; // Toto sa nevykoná
}
```
### Úlohy 5
**5a.** Máš mapu študentov (meno → počet bodov). Napíš funkciu, ktorá pridá body jednému študentovi:
```rust
fn pridaj_body(mapa: &mut HashMap<String, u32>, meno: &str, body: u32) -> bool
```
Vráti `true` ak študent existoval, `false` ak nie.
**5b.** Napíš funkciu, ktorá zdvojnásobí hodnotu pre daný kľúč:
```rust
fn zdvojnasob(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32>
```
Vráti novú hodnotu ak kľúč existoval, None ak nie.
---
## Kapitola 6: Entry API — or_insert
Toto je najdôležitejšia časť. Entry API rieši situáciu: "ak kľúč neexistuje, vlož default; ak existuje, použi existujúcu hodnotu."
```rust
let mut mapa: HashMap<String, i32> = HashMap::new();
// entry() vráti Entry — buď Vacant (prázdny) alebo Occupied (obsadený)
// or_insert(hodnota) — ak Vacant, vlož hodnotu. Vráti &mut na hodnotu.
mapa.entry("Anna".to_string()).or_insert(25);
// Anna neexistovala → vloží 25 → mapa = {"Anna": 25}
mapa.entry("Anna".to_string()).or_insert(99);
// Anna existuje → NEVLOŽÍ 99 → mapa = {"Anna": 25} (nezmenené!)
```
**Hlavné použitie — počítanie výskytov:**
```rust
let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"];
let mut pocty: HashMap<String, usize> = HashMap::new();
for slovo in &slova {
*pocty.entry(slovo.to_string()).or_insert(0) += 1;
}
// {"ahoj": 3, "svet": 2, "rust": 1}
```
Čo sa deje krok po kroku:
```
Slovo "ahoj":
entry("ahoj") → Vacant → or_insert(0) vloží 0 → vráti &mut 0
*(&mut 0) += 1 → hodnota je 1
Slovo "svet":
entry("svet") → Vacant → or_insert(0) vloží 0 → vráti &mut 0
*(&mut 0) += 1 → hodnota je 1
Slovo "ahoj" (druhýkrát):
entry("ahoj") → Occupied (hodnota 1) → or_insert(0) NEVLOŽÍ → vráti &mut 1
*(&mut 1) += 1 → hodnota je 2
```
### Úlohy 6
**6a.** Spočítaj výskyt každého znaku v reťazci `"abrakadabra"`. Použi `entry` + `or_insert`. Výsledok vypíš.
**6b.** Máš vektor čísel `[1, 3, 2, 1, 4, 3, 2, 1, 5]`. Spočítaj koľkokrát sa vyskytuje každé číslo.
**6c.** Čo vypíše? Tipni, potom over:
```rust
let mut m: HashMap<&str, i32> = HashMap::new();
m.entry("a").or_insert(10);
m.entry("b").or_insert(20);
m.entry("a").or_insert(99);
m.insert("b", 99);
println!("{:?}", m.get("a"));
println!("{:?}", m.get("b"));
```
---
## Kapitola 7: Entry API — or_default
`or_default()` je skratka za `or_insert(Default::default())`. Vloží defaultnú hodnotu pre daný typ.
| Typ | Default |
|-----|---------|
| usize, i32, u32... | 0 |
| f64 | 0.0 |
| bool | false |
| String | "" |
| Vec<T> | vec![] |
```rust
let mut pocty: HashMap<String, usize> = HashMap::new();
// Tieto dva riadky robia to isté:
*pocty.entry("ahoj".to_string()).or_insert(0) += 1;
*pocty.entry("ahoj".to_string()).or_default() += 1;
```
**Hlavné použitie — zoskupovanie do vektorov:**
```rust
let mut skupiny: HashMap<String, Vec<String>> = HashMap::new();
// or_default() pre Vec vloží prázdny vektor
skupiny.entry("ovocie".to_string()).or_default().push("jablko".to_string());
skupiny.entry("ovocie".to_string()).or_default().push("hruška".to_string());
skupiny.entry("zelenina".to_string()).or_default().push("mrkva".to_string());
// {"ovocie": ["jablko", "hruška"], "zelenina": ["mrkva"]}
```
### Úlohy 7
**7a.** Máš vektor slov. Zoskup ich podľa prvého písmena do HashMap<char, Vec<String>>:
```rust
let slova = vec!["auto", "ahoj", "banán", "breza", "citrón", "auto"];
// Výsledok: {'a': ["auto", "ahoj", "auto"], 'b': ["banán", "breza"], 'c': ["citrón"]}
```
**7b.** Máš vektor dvojíc (študent, predmet). Zoskup predmety podľa študenta:
```rust
let data = vec![
("Anna", "Matematika"),
("Boris", "Fyzika"),
("Anna", "Fyzika"),
("Boris", "Chémia"),
("Anna", "Chémia"),
];
// Výsledok: {"Anna": ["Matematika", "Fyzika", "Chémia"], "Boris": ["Fyzika", "Chémia"]}
```
---
## Kapitola 8: Entry API — and_modify
`and_modify` ti umožní spraviť niečo s existujúcou hodnotou pred tým, ako sa rozhodne čo robiť:
```rust
let mut mapa: HashMap<String, i32> = HashMap::new();
// Ak existuje → zvýš o 1. Ak neexistuje → vlož 1.
mapa.entry("Anna".to_string())
.and_modify(|v| *v += 1)
.or_insert(1);
// Anna neexistovala → vloží 1
mapa.entry("Anna".to_string())
.and_modify(|v| *v += 1)
.or_insert(1);
// Anna existuje s hodnotou 1 → and_modify zvýši na 2
```
Toto je užitočné keď chceš robiť rôzne veci pre nový vs existujúci kľúč.
### Úlohy 8
**8a.** Máš vektor mien. Pre každé meno: ak sa vyskytlo prvýkrát, vlož `1`. Ak sa vyskytlo znovu, zvýš o `1`. Použi `and_modify` + `or_insert`. (Áno, výsledok je rovnaký ako cez `or_insert(0) += 1`, ale precvič si syntax.)
**8b.** Máš mapu produktov (String → (cena: f64, počet: u32)). Napíš kód, ktorý: ak produkt existuje, zvýši počet o 1. Ak neexistuje, vloží ho s cenou 10.0 a počtom 1.
```rust
let mut produkty: HashMap<String, (f64, u32)> = HashMap::new();
// tvoj kód pre produkt "mlieko"
```
---
## Kapitola 9: Iterátory s HashMap
HashMap sa dá kombinovať s iterátorovými metódami:
```rust
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
mapa.insert("Cyril".to_string(), 17);
// filter — nechaj len dospelých
let dospeli: HashMap<&String, &i32> = mapa.iter()
.filter(|(_, vek)| **vek >= 18)
.collect();
// find — nájdi prvého s daným menom
let boris: Option<(&String, &i32)> = mapa.iter()
.find(|(meno, _)| meno.as_str() == "Boris");
// max_by_key — najstarší
let najstarsi: Option<(&String, &i32)> = mapa.iter()
.max_by_key(|(_, vek)| *vek);
// Kľúče do Vec
let mena: Vec<&String> = mapa.keys().collect();
// Hodnoty — súčet
let sucet_vekov: i32 = mapa.values().sum();
```
### Úlohy 9
**9a.** Máš `HashMap<String, f64>` (produkt → cena). Nájdi najdrahší produkt. Vráť jeho názov ako `Option<&String>`.
**9b.** Máš `HashMap<String, u32>` (mesto → populácia). Vráť Vec miest s populáciou nad 100_000.
**9c.** Máš `HashMap<String, Vec<String>>` (kategória → produkty). Nájdi celkový počet produktov naprieč všetkými kategóriami. Použi `.values()` a `.map()` a `.sum()`.
---
## Kapitola 10: Praktické vzory zo skúšky
### Vzor 1: Počítanie podľa poľa štruktúry
```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
}
```
### Vzor 2: Výpis vo formáte "Kľúč: hodnota"
```rust
fn vypis_statistiky(knihy: &[Kniha]) {
let pocty = pocty_podla_vydavatelstva(knihy);
for (vydavatelstvo, pocet) in &pocty {
println!("{}: {}", vydavatelstvo, pocet);
}
}
```
### Vzor 3: Nájdi najčastejší
```rust
fn najcastejsie_vydavatelstvo(knihy: &[Kniha]) -> Option<String> {
let pocty = pocty_podla_vydavatelstva(knihy);
pocty.into_iter()
.max_by_key(|(_, pocet)| *pocet)
.map(|(vydavatelstvo, _)| vydavatelstvo)
}
```
### Úlohy 10
**10a.** Máš:
```rust
struct Zamestnanec { meno: String, oddelenie: String, plat: u32 }
```
Napíš funkciu `pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, usize>`.
**10b.** Napíš funkciu `celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, u32>` — súčet platov na každom oddelení.
**10c.** Napíš funkciu `vypis_statistiky(zamestnanci: &[Zamestnanec])` — pre každé oddelenie vypíše počet ľudí a celkový plat.
**10d.** Napíš funkciu `oddelenie_s_najvyssim_platom(zamestnanci: &[Zamestnanec]) -> Option<String>` — oddelenie kde je najväčší celkový plat.
---
---
---
# RIEŠENIA
## 1a
```rust
let mut ovocie: HashMap<String, f64> = HashMap::new();
ovocie.insert("Jablko".to_string(), 1.50);
ovocie.insert("Banán".to_string(), 2.00);
ovocie.insert("Pomaranč".to_string(), 2.50);
```
## 1b
```
Some(1)
```
Lebo `"x"` už existoval s hodnotou 1, `insert` ho prepísal na 99 a vrátil starú hodnotu.
## 1c
```rust
let data = vec![("sk", "Slovensko"), ("cz", "Česko"), ("pl", "Poľsko")];
let mapa: HashMap<&str, &str> = data.into_iter().collect();
```
## 2a
```rust
if let Some(cena) = ovocie.get("Jablko") {
println!("Cena: {} €", cena);
} else {
println!("Nemáme");
}
```
## 2b
```rust
fn je_v_mape(mapa: &HashMap<String, i32>, kluc: &str) -> bool {
mapa.contains_key(kluc)
}
```
## 2c
```
Some(1)
None
1
true
false
```
## 3a
```rust
let mut mapa = HashMap::new();
mapa.insert("A".to_string(), 1);
mapa.insert("B".to_string(), 2);
mapa.insert("C".to_string(), 3);
let odstraneny = mapa.remove("B");
println!("{:?}", odstraneny); // Some(2)
println!("{}", mapa.len()); // 2
```
## 3b
```rust
fn odstran_ak_existuje(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32> {
mapa.remove(kluc)
}
```
## 4a
```rust
let mut produkty = HashMap::new();
produkty.insert("Chlieb".to_string(), 1.20);
produkty.insert("Mlieko".to_string(), 0.89);
produkty.insert("Maslo".to_string(), 2.50);
for (nazov, cena) in &produkty {
println!("{}: {} €", nazov, cena);
}
```
## 4b
```rust
fn kluce(mapa: &HashMap<String, i32>) -> Vec<&String> {
mapa.keys().collect()
}
```
## 4c
```rust
fn zvys_vsetky(mapa: &mut HashMap<String, i32>) {
for hodnota in mapa.values_mut() {
*hodnota += 10;
}
}
```
## 4d
```rust
fn sucet_hodnot(mapa: &HashMap<String, i32>) -> i32 {
mapa.values().sum()
}
```
## 5a
```rust
fn pridaj_body(mapa: &mut HashMap<String, u32>, meno: &str, body: u32) -> bool {
if let Some(aktualne) = mapa.get_mut(meno) {
*aktualne += body;
true
} else {
false
}
}
```
## 5b
```rust
fn zdvojnasob(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32> {
let hodnota = mapa.get_mut(kluc)?;
*hodnota *= 2;
Some(*hodnota)
}
```
## 6a
```rust
let text = "abrakadabra";
let mut pocty: HashMap<char, usize> = HashMap::new();
for c in text.chars() {
*pocty.entry(c).or_insert(0) += 1;
}
for (znak, pocet) in &pocty {
println!("{}: {}", znak, pocet);
}
```
## 6b
```rust
let cisla = vec![1, 3, 2, 1, 4, 3, 2, 1, 5];
let mut pocty: HashMap<i32, usize> = HashMap::new();
for c in &cisla {
*pocty.entry(*c).or_insert(0) += 1;
}
// {1: 3, 3: 2, 2: 2, 4: 1, 5: 1}
```
## 6c
```
Some(10)
Some(99)
```
`entry("a").or_insert(99)` — "a" už existuje, takže 99 sa NEVLOŽÍ, ostáva 10.
`insert("b", 99)` — insert VŽDY prepíše, takže "b" je teraz 99.
## 7a
```rust
let slova = vec!["auto", "ahoj", "banán", "breza", "citrón", "auto"];
let mut skupiny: HashMap<char, Vec<String>> = HashMap::new();
for slovo in &slova {
if let Some(prvy) = slovo.chars().next() {
skupiny.entry(prvy).or_default().push(slovo.to_string());
}
}
```
## 7b
```rust
let data = vec![
("Anna", "Matematika"),
("Boris", "Fyzika"),
("Anna", "Fyzika"),
("Boris", "Chémia"),
("Anna", "Chémia"),
];
let mut mapa: HashMap<String, Vec<String>> = HashMap::new();
for (student, predmet) in &data {
mapa.entry(student.to_string()).or_default().push(predmet.to_string());
}
```
## 8a
```rust
let mena = vec!["Anna", "Boris", "Anna", "Cyril", "Boris", "Anna"];
let mut pocty: HashMap<String, i32> = HashMap::new();
for meno in &mena {
pocty.entry(meno.to_string())
.and_modify(|v| *v += 1)
.or_insert(1);
}
// {"Anna": 3, "Boris": 2, "Cyril": 1}
```
## 8b
```rust
let mut produkty: HashMap<String, (f64, u32)> = HashMap::new();
produkty.entry("mlieko".to_string())
.and_modify(|(_, pocet)| *pocet += 1)
.or_insert((10.0, 1));
```
## 9a
```rust
fn najdrahsi(mapa: &HashMap<String, f64>) -> Option<&String> {
mapa.iter()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.map(|(nazov, _)| nazov)
}
```
## 9b
```rust
fn velke_mesta(mapa: &HashMap<String, u32>) -> Vec<&String> {
mapa.iter()
.filter(|(_, pop)| **pop > 100_000)
.map(|(mesto, _)| mesto)
.collect()
}
```
## 9c
```rust
fn celkovy_pocet(mapa: &HashMap<String, Vec<String>>) -> usize {
mapa.values().map(|v| v.len()).sum()
}
```
## 10a
```rust
fn pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, usize> {
let mut mapa = HashMap::new();
for z in zamestnanci {
*mapa.entry(z.oddelenie.clone()).or_insert(0) += 1;
}
mapa
}
```
## 10b
```rust
fn celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, u32> {
let mut mapa: HashMap<String, u32> = HashMap::new();
for z in zamestnanci {
*mapa.entry(z.oddelenie.clone()).or_insert(0) += z.plat;
}
mapa
}
```
## 10c
```rust
fn vypis_statistiky(zamestnanci: &[Zamestnanec]) {
let pocty = pocet_na_oddeleni(zamestnanci);
let platy = celkovy_plat_na_oddeleni(zamestnanci);
for (oddelenie, pocet) in &pocty {
let plat = platy.get(oddelenie).unwrap_or(&0);
println!("{}: {} ľudí, celkový plat: {}", oddelenie, pocet, plat);
}
}
```
## 10d
```rust
fn oddelenie_s_najvyssim_platom(zamestnanci: &[Zamestnanec]) -> Option<String> {
let platy = celkovy_plat_na_oddeleni(zamestnanci);
platy.into_iter()
.max_by_key(|(_, plat)| *plat)
.map(|(oddelenie, _)| oddelenie)
}
```

276
priprava/rust_priprava5.md Normal file
View File

@@ -0,0 +1,276 @@
# BTreeMap — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
```rust
use std::collections::BTreeMap; // toto treba vždy
```
---
## Kapitola 1: Čo to je a rozdiel oproti HashMap
BTreeMap je to isté ako HashMap — kľúč → hodnota — ale s jedným veľkým rozdielom: **kľúče sú vždy zoradené**.
| | HashMap | BTreeMap |
|-|---------|----------|
| Poradie kľúčov | náhodné | zoradené (od najmenšieho) |
| Rýchlosť | O(1) | O(log n) |
| Kedy použiť | väčšinou | keď potrebuješ zoradené kľúče |
| Import | `use std::collections::HashMap` | `use std::collections::BTreeMap` |
```rust
use std::collections::BTreeMap;
let mut mapa = BTreeMap::new();
mapa.insert("Cyril".to_string(), 22);
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// Iterácia — kľúče SÚ zoradené abecedne!
for (meno, vek) in &mapa {
println!("{}: {}", meno, vek);
}
// Anna: 25
// Boris: 30
// Cyril: 22
```
S HashMap by poradie bolo náhodné. S BTreeMap je vždy zoradené.
Pre čísla zoradí od najmenšieho:
```rust
let mut mapa = BTreeMap::new();
mapa.insert(3, "tri");
mapa.insert(1, "jedna");
mapa.insert(2, "dva");
for (k, v) in &mapa {
println!("{}: {}", k, v);
}
// 1: jedna
// 2: dva
// 3: tri
```
### Úlohy 1
**1a.** Vytvor BTreeMap s 5 menami (String) a vekmi (u32). Vypíš ich. Over, že sú zoradené abecedne.
**1b.** Vytvor BTreeMap s číslami ako kľúčmi (i32 → String). Vlož ich v náhodnom poradí. Vypíš a over, že sú zoradené.
**1c.** Vytvor rovnakú mapu raz ako HashMap, raz ako BTreeMap. Vypíš obe. Vidíš rozdiel v poradí?
---
## Kapitola 2: Všetko čo vieš z HashMap funguje rovnako
BTreeMap má **rovnaké API** ako HashMap. Všetko čo si sa naučil, funguje:
```rust
let mut mapa = BTreeMap::new();
// insert — rovnaké
mapa.insert("Anna".to_string(), 25);
let stara = mapa.insert("Anna".to_string(), 26); // Some(25)
// get — rovnaké
let vek = mapa.get("Anna"); // Some(&26)
// contains_key — rovnaké
let je_tam = mapa.contains_key("Anna"); // true
// remove — rovnaké
let odstraneny = mapa.remove("Anna"); // Some(26)
// len, is_empty — rovnaké
let pocet = mapa.len();
let prazdna = mapa.is_empty();
// entry API — rovnaké!
*mapa.entry("Boris".to_string()).or_insert(0) += 1;
mapa.entry("Cyril".to_string()).or_default();
mapa.entry("Dana".to_string())
.and_modify(|v| *v += 1)
.or_insert(1);
// get_mut — rovnaké
if let Some(vek) = mapa.get_mut("Boris") {
*vek += 10;
}
// iterácia — rovnaká syntax, ale ZORADENÉ
for (k, v) in &mapa { /* zoradené podľa kľúča */ }
for k in mapa.keys() { /* zoradené */ }
for v in mapa.values() { /* v poradí podľa zoradených kľúčov */ }
for v in mapa.values_mut() { *v += 1; }
```
Jediný rozdiel je, že výstup je vždy zoradený.
### Úlohy 2
**2a.** Spočítaj výskyt každého znaku v `"abrakadabra"` do BTreeMap. Vypíš. V akom poradí sú znaky?
**2b.** Máš vektor slov. Zoskup ich podľa prvého písmena do `BTreeMap<char, Vec<String>>`. Over, že písmená sú zoradené.
**2c.** Napíš funkciu:
```rust
fn pocitaj_slova(text: &str) -> BTreeMap<String, usize>
```
Počet výskytov každého slova, zoradené abecedne.
---
## Kapitola 3: Range queries — to čo HashMap nevie
Najväčšia výhoda BTreeMap: vieš sa pýtať na **rozsahy** kľúčov.
### range — podmnožina podľa rozsahu kľúčov
```rust
let mut mapa = BTreeMap::new();
mapa.insert(1, "jedna");
mapa.insert(2, "dva");
mapa.insert(3, "tri");
mapa.insert(4, "štyri");
mapa.insert(5, "päť");
mapa.insert(6, "šesť");
mapa.insert(7, "sedem");
// Kľúče od 3 do 5 (vrátane oboch)
for (k, v) in mapa.range(3..=5) {
println!("{}: {}", k, v);
}
// 3: tri
// 4: štyri
// 5: päť
// Kľúče od 3 (bez 6)
for (k, v) in mapa.range(3..6) {
println!("{}: {}", k, v);
}
// 3: tri
// 4: štyri
// 5: päť
// Kľúče od začiatku po 3 (vrátane)
for (k, v) in mapa.range(..=3) {
println!("{}: {}", k, v);
}
// 1: jedna
// 2: dva
// 3: tri
// Kľúče od 5 do konca
for (k, v) in mapa.range(5..) {
println!("{}: {}", k, v);
}
// 5: päť
// 6: šesť
// 7: sedem
```
S Stringami to funguje abecedne:
```rust
let mut mapa = BTreeMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
mapa.insert("Cyril".to_string(), 22);
mapa.insert("Dana".to_string(), 28);
// Mená od "B" po "D" (nezahrnie "D" lebo ..)
for (meno, vek) in mapa.range("B".to_string().."D".to_string()) {
println!("{}: {}", meno, vek);
}
// Boris: 30
// Cyril: 22
```
### Úlohy 3
**3a.** Vytvor BTreeMap s kľúčmi 1 až 10 (i32 → String s názvom čísla). Vypíš len čísla 4 až 7.
**3b.** Máš BTreeMap študentov (meno → priemer). Vypíš len študentov, ktorých meno začína na písmená M až Z. Použi `.range()`.
**3c.** Máš BTreeMap teplôt za dni v mesiaci (u32 → f64, deň → teplota). Napíš funkciu:
```rust
fn teploty_v_tyzdni(teploty: &BTreeMap<u32, f64>, od: u32, do_: u32) -> Vec<f64>
```
Vráti vektor teplôt pre dané dni.
---
## Kapitola 4: first/last — prvý a posledný kľúč
BTreeMap vie efektívne nájsť najmenší a najväčší kľúč:
```rust
let mut mapa = BTreeMap::new();
mapa.insert(5, "päť");
mapa.insert(1, "jedna");
mapa.insert(9, "deväť");
mapa.insert(3, "tri");
// Prvý (najmenší) pár
let prvy = mapa.first_key_value();
// Some((&1, &"jedna"))
// Posledný (najväčší) pár
let posledny = mapa.last_key_value();
// Some((&9, &"deväť"))
// Len kľúče
let prvy_kluc = mapa.keys().next(); // Some(&1)
let posledny_kluc = mapa.keys().last(); // Some(&9)
```
Tieto operácie sú O(log n) — veľmi rýchle. HashMap toto nevie (musel by prejsť celú mapu).
### Úlohy 4
**4a.** Máš BTreeMap študentov (meno → priemer). Nájdi študenta s menom prvým v abecede a posledným v abecede.
**4b.** Máš BTreeMap (rok: u32 → udalosť: String). Nájdi najstaršiu a najnovšiu udalosť.
---
## Kapitola 5: Kedy HashMap, kedy BTreeMap
| Situácia | Použi |
|----------|-------|
| Nepotrebuješ poradie | HashMap (rýchlejší) |
| Chceš zoradený výpis | BTreeMap |
| Potrebuješ range queries | BTreeMap |
| Potrebuješ prvý/posledný | BTreeMap |
| Chceš maximálny výkon | HashMap |
| Na skúške: `vypis_statistiky` zoradene | BTreeMap |
**Tip na skúšku:** Ak zadanie hovorí "vypíš v abecednom poradí" alebo "zoradene", použi BTreeMap. Inak HashMap stačí.
Konverzia medzi nimi je jednoduchá:
```rust
use std::collections::{HashMap, BTreeMap};
// HashMap → BTreeMap
let hash: HashMap<String, i32> = /* ... */;
let btree: BTreeMap<String, i32> = hash.into_iter().collect();
// BTreeMap → HashMap
let btree: BTreeMap<String, i32> = /* ... */;
let hash: HashMap<String, i32> = btree.into_iter().collect();
```
### Úlohy 5
**5a.** Máš:
```rust
struct Zamestnanec { meno: String, oddelenie: String, plat: u32 }
```
Napíš funkciu `pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> BTreeMap<String, usize>` — počet ľudí na oddelení, výstup zoradený podľa názvu oddelenia.
**5b.** Máš vektor slov. Spočítaj výskyt každého slova a výstup zoradeného (BTreeMap). Potom vypíš len slová, ktoré sa vyskytli viac ako 2x.
**5c.** Máš BTreeMap známok študenta (predmet → známka). Napíš funkcie:
- `najlepsia_znamka(znamky: &BTreeMap<String, u8>) -> Option<(&String, &u8)>` — predmet s najlepšou (najnižšou) známkou
- `predmety_v_rozsahu(znamky: &BTreeMap<String, u8>, od: &str, do_: &str) -> Vec<(&String, &u8)>` — predmety s názvom v danom abecednom rozsahu

474
priprava/rust_priprava6.md Normal file
View File

@@ -0,0 +1,474 @@
# HashSet — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
```rust
use std::collections::HashSet; // toto treba vždy
```
---
## Kapitola 1: Čo to je a vytváranie
HashSet je kolekcia **unikátnych hodnôt**. Žiadna hodnota sa neopakuje. Negarantuje poradie.
Rozdiel oproti Vec: Vec môže mať duplikáty a pamätá poradie.
Rozdiel oproti HashMap: HashMap má kľúč → hodnota. HashSet má len hodnotu (žiadny pár).
```rust
// Prázdny set
let mut mnozina: HashSet<i32> = HashSet::new();
// Alebo nechaj Rust odvodiť:
let mut mnozina = HashSet::new();
mnozina.insert(1); // teraz Rust vie: HashSet<i32>
// Z vektora — duplikáty zmiznú!
let cisla = vec![1, 2, 3, 2, 1, 4, 3, 5];
let unikatne: HashSet<i32> = cisla.into_iter().collect();
// {1, 2, 3, 4, 5} — len 5 prvkov, nie 8
// Z reťazca — množina znakov
let znaky: HashSet<char> = "abrakadabra".chars().collect();
// {'a', 'b', 'r', 'k', 'd'} — len 5 unikátnych
```
### Úlohy 1
**1a.** Vytvor HashSet čísel z vektora `[5, 3, 5, 1, 3, 2, 1, 4]`. Vypíš koľko prvkov má. Vypíš prvky.
**1b.** Vytvor HashSet znakov z reťazca `"hello world"`. Koľko unikátnych znakov to má? (vrátane medzery)
**1c.** Čo vypíše? Tipni, potom over:
```rust
let v = vec![10, 20, 10, 30, 20, 10];
let s: HashSet<i32> = v.into_iter().collect();
println!("{}", s.len());
```
---
## Kapitola 2: insert — vráti bool!
Toto je kľúčový rozdiel oproti HashMap. `insert` vráti `bool`:
- `true` → prvok bol **nový**, vložil sa
- `false` → prvok už **existoval**, nič sa nezmenilo
```rust
let mut mnozina = HashSet::new();
let novy = mnozina.insert("Anna".to_string());
// novy = true — Anna bola pridaná
let novy = mnozina.insert("Anna".to_string());
// novy = false — Anna už existuje, nič sa nezmenilo
```
**Praktické — detekcia duplikátov:**
```rust
let mut videne = HashSet::new();
let slova = vec!["ahoj", "svet", "ahoj", "rust"];
for slovo in &slova {
if !videne.insert(*slovo) {
println!("Duplikát: {}", slovo);
}
}
// Vypíše: Duplikát: ahoj
```
Logika: `insert` vráti `false` → prvok tam už bol → je to duplikát.
### Úlohy 2
**2a.** Napíš funkciu `ma_duplikaty(cisla: &[i32]) -> bool`. Použi HashSet a insert. Vráť true hneď ako nájdeš prvý duplikát.
**2b.** Napíš funkciu `najdi_duplikaty(cisla: &[i32]) -> Vec<i32>` — vráti vektor všetkých duplikovaných hodnôt (každý duplikát len raz). Hint: použi dva HashSety — `videne` a `duplikaty`.
**2c.** Čo vypíše? Tipni:
```rust
let mut s = HashSet::new();
println!("{}", s.insert(1));
println!("{}", s.insert(2));
println!("{}", s.insert(1));
println!("{}", s.insert(3));
println!("{}", s.insert(2));
```
---
## Kapitola 3: contains, remove, len
```rust
let mut mnozina = HashSet::new();
mnozina.insert("Anna".to_string());
mnozina.insert("Boris".to_string());
// contains — je tam?
mnozina.contains("Anna"); // true
mnozina.contains("Cyril"); // false
// len — počet prvkov
mnozina.len(); // 2
// is_empty
mnozina.is_empty(); // false
// remove — vráti bool (true ak existoval a bol odstránený)
mnozina.remove("Anna"); // true — Anna odstránená
mnozina.remove("Cyril"); // false — Cyril tam ani nebol
// clear — vymaž všetko
mnozina.clear();
```
**Dôležité:** `contains` na HashSet je O(1) — veľmi rýchle. Na Vec je O(n) — pomalé. Ak často kontroluješ existenciu, použi HashSet.
```rust
// POMALÉ — Vec.contains prechádza celý vektor
let povolene = vec!["admin", "editor", "viewer"];
povolene.contains(&"admin"); // O(n)
// RÝCHLE — HashSet.contains
let povolene: HashSet<&str> = vec!["admin", "editor", "viewer"].into_iter().collect();
povolene.contains("admin"); // O(1)
```
### Úlohy 3
**3a.** Vytvor HashSet povolených farieb: "červená", "modrá", "zelená". Napíš funkciu:
```rust
fn je_povolena(povolene: &HashSet<String>, farba: &str) -> bool
```
**3b.** Napíš funkciu `pocet_unikatnych(cisla: &[i32]) -> usize` — koľko rôznych hodnôt je vo vektore.
**3c.** Máš HashSet mien. Odstráň všetky mená kratšie ako 4 znaky. Použi `.retain()`:
```rust
// retain nechá len prvky, kde closure vráti true
mnozina.retain(|meno| meno.len() >= 4);
```
---
## Kapitola 4: Iterácia
```rust
let mnozina: HashSet<i32> = vec![3, 1, 4, 1, 5].into_iter().collect();
// Základná iterácia — poradie NIE JE garantované!
for prvok in &mnozina {
println!("{}", prvok);
}
// filter + collect — vyfiltruj do nového HashSetu
let parne: HashSet<&i32> = mnozina.iter().filter(|c| *c % 2 == 0).collect();
// Do Vec
let vektor: Vec<&i32> = mnozina.iter().collect();
// any — existuje prvok splňajúci podmienku?
let ma_velke: bool = mnozina.iter().any(|c| *c > 3);
// count s filtrom
let pocet_parnych: usize = mnozina.iter().filter(|c| *c % 2 == 0).count();
// sum
let sucet: i32 = mnozina.iter().sum();
```
### Úlohy 4
**4a.** Máš HashSet mien (String). Vypíš len mená začínajúce na 'A'.
**4b.** Napíš funkciu `sucet(mnozina: &HashSet<i32>) -> i32` — súčet všetkých prvkov.
**4c.** Napíš funkciu `najdlhsi(mnozina: &HashSet<String>) -> Option<&String>` — najdlhší reťazec. Použi `.max_by_key()`.
**4d.** Máš HashSet čísel. Koľko z nich je deliteľných 3? Napíš to jedným výrazom.
---
## Kapitola 5: Prienik (intersection)
Prienik = prvky, ktoré sú v **oboch** množinách.
```rust
let a: HashSet<i32> = vec![1, 2, 3, 4].into_iter().collect();
let b: HashSet<i32> = vec![3, 4, 5, 6].into_iter().collect();
// intersection vráti iterátor cez &i32
let prienik: HashSet<&i32> = a.intersection(&b).collect();
// {&3, &4}
// Ak chceš vlastné hodnoty (nie referencie), použi .copied()
let prienik: HashSet<i32> = a.intersection(&b).copied().collect();
// {3, 4}
// Alebo do Vec
let prienik: Vec<&i32> = a.intersection(&b).collect();
// [&3, &4]
```
**Skratka operátorom:**
```rust
let prienik: HashSet<i32> = &a & &b;
// {3, 4} — rovnaký výsledok, kratší zápis
```
### Úlohy 5
**5a.** Máš dva zoznamy mien (Vec<String>). Preveď ich na HashSety a nájdi spoločné mená.
```rust
let trieda_a = vec!["Anna", "Boris", "Cyril", "Dana"];
let trieda_b = vec!["Boris", "Dana", "Emil", "Fero"];
// Spoločné: Boris, Dana
```
**5b.** Napíš funkciu:
```rust
fn spolocne_pismena(slovo1: &str, slovo2: &str) -> HashSet<char>
```
Vráti písmená, ktoré sú v oboch slovách.
**5c.** Máš 3 HashSety. Nájdi prvky, ktoré sú vo všetkých troch. Hint: najprv prienik A∩B, potom výsledok ∩ C.
---
## Kapitola 6: Zjednotenie (union)
Zjednotenie = prvky, ktoré sú v **aspoň jednej** z množín. Duplikáty sa nezopakujú.
```rust
let a: HashSet<i32> = vec![1, 2, 3].into_iter().collect();
let b: HashSet<i32> = vec![3, 4, 5].into_iter().collect();
let zjednotenie: HashSet<&i32> = a.union(&b).collect();
// {&1, &2, &3, &4, &5}
let zjednotenie: HashSet<i32> = a.union(&b).copied().collect();
// {1, 2, 3, 4, 5}
```
**Skratka:**
```rust
let zjednotenie: HashSet<i32> = &a | &b;
```
### Úlohy 6
**6a.** Máš záujmy dvoch ľudí ako HashSety. Nájdi všetky záujmy dokopy (bez duplikátov).
**6b.** Napíš funkciu:
```rust
fn vsetky_unikatne_znaky(texty: &[String]) -> HashSet<char>
```
Vráti množinu všetkých znakov zo všetkých textov. Hint: iteruj cez texty a postupne rob union, alebo jednoducho insertuj do jedného HashSetu.
---
## Kapitola 7: Rozdiel (difference)
Rozdiel = prvky v **prvej**, ktoré nie sú v **druhej**.
```rust
let a: HashSet<i32> = vec![1, 2, 3, 4].into_iter().collect();
let b: HashSet<i32> = vec![3, 4, 5, 6].into_iter().collect();
// Prvky v A, ktoré NIE SÚ v B
let rozdiel: HashSet<i32> = a.difference(&b).copied().collect();
// {1, 2}
// Opačný smer — prvky v B, ktoré NIE SÚ v A
let rozdiel_opacny: HashSet<i32> = b.difference(&a).copied().collect();
// {5, 6}
```
**Skratka:**
```rust
let rozdiel: HashSet<i32> = &a - &b; // {1, 2}
```
**Symetrický rozdiel** = prvky v jednej ALEBO druhej, ale nie v oboch:
```rust
let sym: HashSet<i32> = a.symmetric_difference(&b).copied().collect();
// {1, 2, 5, 6}
// Skratka:
let sym: HashSet<i32> = &a ^ &b;
```
### Úlohy 7
**7a.** Máš zoznam všetkých študentov a zoznam prítomných. Nájdi kto chýba:
```rust
let vsetci: HashSet<String> = /* Anna, Boris, Cyril, Dana, Emil */;
let pritomni: HashSet<String> = /* Anna, Cyril, Emil */;
// Chýba: Boris, Dana
```
**7b.** Napíš funkciu:
```rust
fn chybajuce_pismena(slovo: &str) -> HashSet<char>
```
Vráti písmená abecedy (a-z), ktoré sa v slove nenachádzajú. Hint: `('a'..='z').collect()` ti dá HashSet celej abecedy.
**7c.** Máš dva Vec<i32>. Nájdi čísla, ktoré sú len v jednom z nich (nie v oboch). Použi symetrický rozdiel.
---
## Kapitola 8: Podmnožina, nadmnožina, disjunkcia
```rust
let mala: HashSet<i32> = vec![1, 2].into_iter().collect();
let velka: HashSet<i32> = vec![1, 2, 3, 4].into_iter().collect();
let ina: HashSet<i32> = vec![5, 6].into_iter().collect();
// is_subset — je mala podmnožinou velka?
mala.is_subset(&velka); // true — všetky prvky mala sú vo velka
// is_superset — je velka nadmnožinou mala?
velka.is_superset(&mala); // true — velka obsahuje všetko z mala
// is_disjoint — nemajú nič spoločné?
mala.is_disjoint(&ina); // true — žiadny spoločný prvok
mala.is_disjoint(&velka); // false — majú spoločné 1 a 2
```
### Úlohy 8
**8a.** Máš povinné predmety a predmety, ktoré študent absolvoval. Napíš funkciu:
```rust
fn splnil_vsetky(povinne: &HashSet<String>, absolvovane: &HashSet<String>) -> bool
```
Vráti true ak absolvoval všetky povinné. Jeden riadok.
**8b.** Máš dve skupiny ľudí. Napíš funkciu, ktorá zistí, či sa skupiny vôbec neprekrývajú:
```rust
fn su_nezavisle(a: &HashSet<String>, b: &HashSet<String>) -> bool
```
**8c.** Študent má predmety. Máš set povinných, set voliteľných. Napíš:
- `splnil_povinne(studentove: &HashSet<String>, povinne: &HashSet<String>) -> bool`
- `ktore_povinne_chybaju(studentove: &HashSet<String>, povinne: &HashSet<String>) -> HashSet<String>` — ktoré povinné mu ešte chýbajú
---
## Kapitola 9: Prehľad operátorov
Všetky množinové operácie majú aj skrátený zápis:
```rust
let a: HashSet<i32> = vec![1, 2, 3].into_iter().collect();
let b: HashSet<i32> = vec![2, 3, 4].into_iter().collect();
let prienik = &a & &b; // {2, 3}
let zjednotenie = &a | &b; // {1, 2, 3, 4}
let rozdiel = &a - &b; // {1}
let sym_rozdiel = &a ^ &b; // {1, 4}
```
Všetky vracajú nový `HashSet<i32>` (nie referencie). Pozor na `&a` — treba referencie.
### Úlohy 9
**9a.** Máš tri sety A, B, C. Napíš jedným výrazom s operátormi:
- prvky, ktoré sú vo všetkých troch
- prvky, ktoré sú v aspoň jednom
- prvky, ktoré sú v A ale nie v B ani v C
---
## Kapitola 10: Praktické vzory zo skúšky
### Vzor 1: Obesenec — sledovanie uhádnutých písmen
```rust
struct Obesenec {
slovo: String,
uhadnute: HashSet<char>,
skusane: HashSet<char>,
zivoty: u8,
}
impl Obesenec {
fn new(slovo: &str) -> Obesenec {
Obesenec {
slovo: slovo.to_lowercase(),
uhadnute: HashSet::new(),
skusane: HashSet::new(),
zivoty: 6,
}
}
fn tipni(&mut self, pismeno: char) -> &str {
let p = pismeno.to_lowercase().next().unwrap();
// insert vráti false ak písmeno už bolo skúšané
if !self.skusane.insert(p) {
return "Už si skúšal";
}
if self.slovo.contains(p) {
self.uhadnute.insert(p);
"Správne"
} else {
self.zivoty -= 1;
"Zle"
}
}
// Zobraz slovo: uhádnuté písmená, neuhádnuté ako '_'
fn zobraz(&self) -> String {
self.slovo.chars().map(|c| {
if self.uhadnute.contains(&c) { c } else { '_' }
}).collect()
}
// Vyhral ak všetky písmená slova sú v uhadnute
fn vyhral(&self) -> bool {
self.slovo.chars().all(|c| self.uhadnute.contains(&c))
}
}
```
Kľúčové veci:
- `skusane.insert(p)` vráti `false` → písmeno už bolo → preskočíme
- `uhadnute.contains(&c)` → rýchla kontrola pre každý znak slova
- `slovo.chars().all(|c| uhadnute.contains(&c))` → vyhral ak uhádol všetky
### Vzor 2: Unikátne hodnoty z kolekcie štruktúr
```rust
struct Kniha { nazov: String, zaner: String }
fn unikatne_zanre(knihy: &[Kniha]) -> HashSet<String> {
knihy.iter().map(|k| k.zaner.clone()).collect()
}
```
### Vzor 3: Rýchle vyhľadávanie v cykle
```rust
// Ak kontroluješ contains opakovane, preveď Vec na HashSet
fn najdi_spolocne(kniznica: &[Kniha], pozicane_nazvy: &[String]) -> Vec<&Kniha> {
let nazvy_set: HashSet<&String> = pozicane_nazvy.iter().collect();
kniznica.iter()
.filter(|k| nazvy_set.contains(&k.nazov))
.collect()
}
```
### Úlohy 10
**10a.** Implementuj Obesenca sám od nuly. Musíš mať:
- `new(slovo: &str) -> Obesenec`
- `tipni(&mut self, pismeno: char) -> bool` — true ak správne
- `zobraz(&self) -> String` — slovo s '_' za neuhádnuté
- `je_koniec(&self) -> bool` — true ak vyhral alebo zivoty == 0
**10b.** Máš vektor študentov s `predmety: Vec<String>`. Napíš:
- `vsetky_predmety(studenti: &[Student]) -> HashSet<String>`
- `studenti_s_predmetom(studenti: &[Student], predmet: &str) -> Vec<&Student>`
**10c.** Máš `povinne_predmety: HashSet<String>` a `absolvovane: HashSet<String>`. Vypíš: koľko povinných splnil, koľko mu chýba, ktoré mu chýbajú.

540
priprava/rust_priprava7.md Normal file
View File

@@ -0,0 +1,540 @@
# itertools — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
```rust
use itertools::Itertools; // toto treba vždy
```
`Cargo.toml`:
```toml
[dependencies]
itertools = "0.14"
```
itertools pridáva metódy na každý iterátor. Nič špeciálne netreba robiť — len `use itertools::Itertools` a všetky metódy sú k dispozícii na `.iter()`, `.into_iter()` atď.
---
## Kapitola 1: sorted — zoradenie iterátora
Štandardný Rust nemá `.sorted()` na iterátoroch. itertools ho pridáva.
```rust
use itertools::Itertools;
let cisla = vec![3, 1, 4, 1, 5, 9, 2, 6];
// sorted() — zoradí vzostupne, vráti nový iterátor
let zoradene: Vec<i32> = cisla.iter().copied().sorted().collect();
// [1, 1, 2, 3, 4, 5, 6, 9]
// sorted_by — vlastné porovnanie
let zostupne: Vec<i32> = cisla.iter().copied()
.sorted_by(|a, b| b.cmp(a))
.collect();
// [9, 6, 5, 4, 3, 2, 1, 1]
// sorted_by_key — zoraď podľa kľúča
let slova = vec!["banán", "auto", "citrón"];
let podla_dlzky: Vec<&str> = slova.into_iter()
.sorted_by_key(|s| s.len())
.collect();
// ["auto", "banán", "citrón"]
```
So štruktúrami:
```rust
struct Student { meno: String, priemer: f32 }
let studenti = vec![
Student { meno: "Cyril".into(), priemer: 1.5 },
Student { meno: "Anna".into(), priemer: 2.1 },
Student { meno: "Boris".into(), priemer: 1.2 },
];
// Zoraď podľa priemeru
let zoradeni: Vec<&Student> = studenti.iter()
.sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap())
.collect();
// Boris (1.2), Cyril (1.5), Anna (2.1)
// Zoraď podľa mena abecedne
let abecedne: Vec<&Student> = studenti.iter()
.sorted_by_key(|s| &s.meno)
.collect();
// Anna, Boris, Cyril
```
**Bez itertools** by si musel klonovať do Vec a volať `.sort()`:
```rust
// Bez itertools — zbytočne komplikované
let mut kopie = studenti.clone();
kopie.sort_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap());
// S itertools — jednoduché reťazenie
let vysledok: Vec<&Student> = studenti.iter()
.sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap())
.collect();
```
### Úlohy 1
**1a.** Máš vektor mien. Zoraď ich abecedne pomocou `.sorted()`. Vypíš.
**1b.** Máš vektor čísel. Zoraď zostupne pomocou `.sorted_by()`.
**1c.** Máš vektor reťazcov. Zoraď podľa dĺžky (najkratšie prvé) pomocou `.sorted_by_key()`.
**1d.** Máš `Vec<(String, u32)>` — mená a veky. Zoraď podľa veku zostupne.
---
## Kapitola 2: unique — odstránenie duplikátov
```rust
use itertools::Itertools;
let cisla = vec![1, 3, 2, 1, 4, 3, 2, 1, 5];
// unique() — odstráni duplikáty, zachová prvý výskyt
let unikatne: Vec<&i32> = cisla.iter().unique().collect();
// [&1, &3, &2, &4, &5] — poradie prvého výskytu je zachované!
// S owned hodnotami
let unikatne: Vec<i32> = cisla.into_iter().unique().collect();
// [1, 3, 2, 4, 5]
```
Rozdiel oproti HashSet: `unique()` **zachováva poradie** prvého výskytu. HashSet nie.
```rust
// unique_by — odstráni duplikáty podľa kľúča
struct Student { meno: String, trieda: String }
let studenti = vec![
Student { meno: "Anna".into(), trieda: "A".into() },
Student { meno: "Boris".into(), trieda: "B".into() },
Student { meno: "Cyril".into(), trieda: "A".into() },
Student { meno: "Dana".into(), trieda: "B".into() },
];
// Jeden študent z každej triedy (prvý nájdený)
let po_jednom: Vec<&Student> = studenti.iter()
.unique_by(|s| &s.trieda)
.collect();
// Anna (trieda A), Boris (trieda B) — Cyril a Dana sú duplikáty tried
```
### Úlohy 2
**2a.** Máš vektor slov s duplikátmi. Odstráň duplikáty a zachovaj poradie.
**2b.** Máš vektor `(String, i32)` — (meno, vek). Niektoré mená sa opakujú. Nechaj len prvý výskyt každého mena. Použi `unique_by`.
**2c.** Máš vektor reťazcov. Chceš unikátne slová case-insensitive (t.j. "Ahoj" a "ahoj" sú duplikáty). Použi `unique_by` s `.to_lowercase()`.
---
## Kapitola 3: join — spojenie do reťazca
```rust
use itertools::Itertools;
let slova = vec!["ahoj", "svet", "rust"];
// join — spojí prvky oddeľovačom
let veta: String = slova.iter().join(", ");
// "ahoj, svet, rust"
let cisla = vec![1, 2, 3, 4, 5];
let text: String = cisla.iter().join(" - ");
// "1 - 2 - 3 - 4 - 5"
// Prázdny oddeľovač
let spolu: String = slova.iter().join("");
// "ahojsvetrust"
// Funguje s čímkoľvek čo implementuje Display
let floaty = vec![1.5, 2.7, 3.14];
let text: String = floaty.iter().join(", ");
// "1.5, 2.7, 3.14"
```
**Bez itertools** by si to robil cez `.collect::<Vec<String>>().join(",")` alebo ručne.
### Úlohy 3
**3a.** Máš vektor mien. Spoj ich čiarkou a medzerou do jedného reťazca.
**3b.** Máš vektor čísel 1 až 10. Spoj ich pomlčkou.
**3c.** Máš vektor štruktúr `Kniha { nazov: String }`. Spoj názvy kníh do jedného reťazca oddeleného " | ". Hint: najprv `.map()` na názvy, potom `.join()`.
---
## Kapitola 4: counts — počítanie výskytov
```rust
use itertools::Itertools;
let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"];
// counts() — vráti HashMap<T, usize> s počtom výskytov
let pocty = slova.iter().counts();
// {&"ahoj": 3, &"svet": 2, &"rust": 1}
// counts_by — počítaj podľa kľúča
let text = "Hello World";
let pocty = text.chars().counts_by(|c| c.is_uppercase());
// {true: 2, false: 9} (H a W sú veľké)
```
Toto nahrádza celý ten entry().or_insert(0) vzor:
```rust
// BEZ itertools — 4 riadky
let mut pocty = HashMap::new();
for slovo in &slova {
*pocty.entry(slovo).or_insert(0) += 1;
}
// S itertools — 1 riadok
let pocty = slova.iter().counts();
```
### Úlohy 4
**4a.** Máš reťazec. Spočítaj výskyt každého znaku. Použi `.chars().counts()`.
**4b.** Máš vektor štruktúr `Kniha { nazov: String, zaner: String }`. Spočítaj koľko kníh je v každom žánri. Použi `.map()` + `.counts()`.
**4c.** Máš vektor čísel. Spočítaj koľko je párnych a koľko nepárnych. Použi `.counts_by()`.
---
## Kapitola 5: group_by — zoskupovanie
**Dôležité:** `group_by` funguje len na **už zoradených** dátach! Zoskupí po sebe idúce prvky s rovnakým kľúčom.
```rust
use itertools::Itertools;
// Dáta MUSIA byť zoradené podľa rovnakého kľúča!
let cisla = vec![1, 1, 2, 2, 2, 3, 3, 1];
// group_by zoskupí PO SEBE IDÚCE prvky
for (kluc, skupina) in &cisla.iter().group_by(|&&c| c) {
let prvky: Vec<&&i32> = skupina.collect();
println!("{}: {:?}", kluc, prvky);
}
// 1: [&&1, &&1]
// 2: [&&2, &&2, &&2]
// 3: [&&3, &&3]
// 1: [&&1] ← POZOR! Posledná 1 je v NOVEJ skupine!
```
Ak chceš všetky rovnaké spolu, musíš najprv zoradiť:
```rust
let cisla = vec![1, 3, 2, 1, 2, 3, 1];
// Najprv zoradiť, potom group_by
for (kluc, skupina) in &cisla.iter().sorted().group_by(|&&c| c) {
let pocet = skupina.count();
println!("{}: {}x", kluc, pocet);
}
// 1: 3x
// 2: 2x
// 3: 2x
```
So štruktúrami:
```rust
struct Kniha { nazov: String, zaner: String }
let knihy = vec![/* ... */];
// Zoskup podľa žánru — musíš najprv zoradiť!
let zoradene: Vec<&Kniha> = knihy.iter()
.sorted_by_key(|k| &k.zaner)
.collect();
for (zaner, skupina) in &zoradene.iter().group_by(|k| &k.zaner) {
let knihy_v_zanri: Vec<&&&Kniha> = skupina.collect();
println!("{}: {} kníh", zaner, knihy_v_zanri.len());
}
```
**Tip:** Pre jednoduché počítanie použi radšej `.counts()` z kapitoly 4. `group_by` je užitočný keď chceš s celou skupinou niečo robiť (nie len počítať).
### Úlohy 5
**5a.** Máš vektor slov zoradený abecedne. Zoskup podľa prvého písmena a vypíš koľko slov začína na ktoré písmeno.
**5b.** Máš vektor čísel. Zoraď ich a potom zoskup rovnaké čísla. Pre každú skupinu vypíš číslo a koľkokrát sa vyskytuje.
---
## Kapitola 6: chunk_by a chunks
### chunks — rozdeľ na kúsky pevnej veľkosti
```rust
use itertools::Itertools;
let cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// chunks(3) — skupiny po 3
for chunk in &cisla.iter().chunks(3) {
let skupina: Vec<&i32> = chunk.collect();
println!("{:?}", skupina);
}
// [1, 2, 3]
// [4, 5, 6]
// [7, 8, 9]
// [10] ← posledná skupina môže byť menšia
```
### tuple_windows — posuvné okno
```rust
let cisla = vec![1, 2, 3, 4, 5];
// Dvojice po sebe idúcich
for (a, b) in cisla.iter().tuple_windows() {
println!("{} a {}", a, b);
}
// 1 a 2
// 2 a 3
// 3 a 4
// 4 a 5
// Trojice
for (a, b, c) in cisla.iter().tuple_windows() {
println!("{}, {}, {}", a, b, c);
}
// 1, 2, 3
// 2, 3, 4
// 3, 4, 5
```
### Úlohy 6
**6a.** Máš vektor 20 študentov. Rozdeľ ich do skupín po 5. Vypíš každú skupinu.
**6b.** Máš vektor denných teplôt. Vypočítaj priemernú teplotu za každý 7-dňový úsek (chunks(7)).
**6c.** Máš vektor čísel. Použi `tuple_windows` na nájdenie všetkých párov po sebe idúcich čísel, kde druhé je väčšie ako prvé (rastúce páry).
---
## Kapitola 7: min_set, max_set — všetky minimá/maximá
Štandardný `.min()` vráti len jedno minimum. Čo ak je ich viac?
```rust
use itertools::Itertools;
let cisla = vec![3, 1, 4, 1, 5, 1, 2];
// min_set — vráti Vec VŠETKÝCH miním
let minima: Vec<&i32> = cisla.iter().min_set();
// [&1, &1, &1] — tri jednotky
// max_set
let maxima: Vec<&i32> = cisla.iter().max_set();
// [&5]
// min_set_by_key — so štruktúrami
struct Student { meno: String, priemer: f32 }
let studenti = vec![/* ... */];
// Všetci študenti s najlepším priemerom
let najlepsi: Vec<&Student> = studenti.iter()
.min_set_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap());
```
### Úlohy 7
**7a.** Máš vektor čísel. Nájdi všetky výskyty maximálnej hodnoty. Koľko ich je?
**7b.** Máš vektor slov. Nájdi všetky najdlhšie slová (môže ich byť viac rovnako dlhých). Použi `max_set_by_key`.
---
## Kapitola 8: interleave a zip_eq
### interleave — striedavo z dvoch iterátorov
```rust
use itertools::Itertools;
let a = vec![1, 3, 5];
let b = vec![2, 4, 6];
let striedavo: Vec<i32> = a.into_iter().interleave(b.into_iter()).collect();
// [1, 2, 3, 4, 5, 6]
let x = vec!["a", "b", "c"];
let y = vec!["1", "2", "3"];
let mix: Vec<&str> = x.into_iter().interleave(y.into_iter()).collect();
// ["a", "1", "b", "2", "c", "3"]
```
### zip_eq — zip ktorý panicky ak rôzna dĺžka
Štandardný `.zip()` ticho skráti na kratší. `zip_eq` ti povie ak majú rôznu dĺžku:
```rust
let mena = vec!["Anna", "Boris", "Cyril"];
let veky = vec![25, 30, 22];
// Štandardný zip — OK
let pary: Vec<_> = mena.iter().zip(veky.iter()).collect();
// zip_eq — rovnaké, ale panikne ak rôzna dĺžka
let pary: Vec<_> = mena.iter().zip_eq(veky.iter()).collect();
// let veky_kratsi = vec![25, 30];
// mena.iter().zip_eq(veky_kratsi.iter()).collect(); // PANIC!
```
### Úlohy 8
**8a.** Máš vektor otázok a vektor odpovedí. Spáruj ich a vypíš "Otázka: ... → Odpoveď: ...". Použi `zip_eq` (chceš aby program spadol ak nesedia počty).
**8b.** Máš dva vektory — párne a nepárne čísla. Prelož ich striedavo do jedného vektora.
---
## Kapitola 9: format_with a positions
### positions — indexy prvkov splňajúcich podmienku
```rust
use itertools::Itertools;
let cisla = vec![10, 25, 30, 5, 40, 15];
// Indexy prvkov > 20
let indexy: Vec<usize> = cisla.iter().positions(|c| *c > 20).collect();
// [1, 2, 4] (25 je na indexe 1, 30 na 2, 40 na 4)
```
Toto je jednoduchšie ako `.enumerate().filter().map()`:
```rust
// BEZ itertools — dlhé
let indexy: Vec<usize> = cisla.iter().enumerate()
.filter(|(_, c)| **c > 20)
.map(|(i, _)| i)
.collect();
// S itertools — krátke
let indexy: Vec<usize> = cisla.iter().positions(|c| *c > 20).collect();
```
### Úlohy 9
**9a.** Máš vektor slov. Nájdi pozície (indexy) všetkých slov dlhších ako 5 znakov.
**9b.** Máš vektor študentov s priemerom. Nájdi indexy študentov s priemerom pod 1.5.
---
## Kapitola 10: Kombinácie na skúške
### Vzor 1: Zoradený výpis štatistík
```rust
use itertools::Itertools;
struct Kniha { nazov: String, vydavatelstvo: String }
fn vypis_statistiky(knihy: &[Kniha]) {
// counts + sorted — zoradený výpis počtov
let pocty = knihy.iter().map(|k| &k.vydavatelstvo).counts();
for (vydavatelstvo, pocet) in pocty.iter().sorted_by_key(|(_, p)| std::cmp::Reverse(*p)) {
println!("{}: {}", vydavatelstvo, pocet);
}
}
```
### Vzor 2: Unikátne zoradené hodnoty
```rust
fn unikatne_zanre_zoradene(knihy: &[Kniha]) -> Vec<String> {
knihy.iter()
.map(|k| k.vydavatelstvo.clone())
.unique()
.sorted()
.collect()
}
```
### Vzor 3: Top N
```rust
fn top_3_studenti(studenti: &[Student]) -> Vec<&Student> {
studenti.iter()
.sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap())
.take(3)
.collect()
}
```
### Vzor 4: Formátovaný výpis zoznamu
```rust
fn vypis_mena(studenti: &[Student]) -> String {
studenti.iter().map(|s| &s.meno).join(", ")
}
// "Anna, Boris, Cyril"
```
### Vzor 5: Zoskupenie a štatistiky
```rust
fn priemer_podla_triedy(studenti: &[Student]) {
for (trieda, skupina) in &studenti.iter()
.sorted_by_key(|s| &s.trieda)
.group_by(|s| &s.trieda)
{
let zoznam: Vec<&Student> = skupina.collect();
let sucet: f32 = zoznam.iter().map(|s| s.priemer).sum();
let priemer = sucet / zoznam.len() as f32;
println!("Trieda {}: priemer {:.2}", trieda, priemer);
}
}
```
### Úlohy 10
**10a.** Máš vektor `Zamestnanec { meno: String, oddelenie: String, plat: u32 }`. Napíš funkciu, ktorá:
1. Zoskupí zamestnancov podľa oddelenia
2. Pre každé oddelenie vypíše mená zoradené abecedne, spojené čiarkou
3. Oddelenia vypíše zoradené abecedne
Použi: `sorted_by_key`, `group_by`, `join`.
**10b.** Máš vektor kníh. Nájdi 5 najčastejších žánrov. Použi `counts` + `sorted_by_key` + `take`.
**10c.** Máš vektor čísel. Použi `tuple_windows` na zistenie najväčšieho rozdielu medzi dvoma po sebe idúcimi číslami.
**10d.** Máš vektor reťazcov s duplikátmi. Vyfiltruj unikátne, zoraď abecedne, spoj do jedného reťazca oddeleného " | ". Všetko jedným reťazením.
---
## Prehľad: kedy čo z itertools
| Chcem... | Metóda |
|----------|--------|
| Zoradiť iterátor | `.sorted()`, `.sorted_by()`, `.sorted_by_key()` |
| Odstrániť duplikáty (so zachovaním poradia) | `.unique()`, `.unique_by()` |
| Spojiť do reťazca | `.join(separator)` |
| Spočítať výskyty → HashMap | `.counts()`, `.counts_by()` |
| Zoskupiť po sebe idúce | `.group_by()` (najprv sorted!) |
| Rozdeliť na kúsky | `.chunks(n)` |
| Posuvné okno | `.tuple_windows()` |
| Všetky minimá/maximá | `.min_set()`, `.max_set()` |
| Preložiť dva iterátory | `.interleave()` |
| Zip s kontrolou dĺžky | `.zip_eq()` |
| Indexy splňajúce podmienku | `.positions()` |

844
priprava/rust_priprava8.md Normal file
View File

@@ -0,0 +1,844 @@
# String, &str, char — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
---
## Kapitola 1: Čo je čo
V Ruste existujú 3 typy pre prácu s textom:
| Typ | Čo to je | Vlastní dáta? | Veľkosť |
|-----|----------|---------------|---------|
| `String` | Vlastnený, meniteľný reťazec na heape | Áno | Rastie/zmenšuje sa |
| `&str` | Referencia na kúsok textu (string slice) | Nie (len pozerá) | Fixná |
| `char` | Jeden Unicode znak | Áno | 4 bajty vždy |
```rust
// String — vlastní dáta, môžeš meniť
let mut s: String = String::from("ahoj");
s.push_str(" svet"); // OK — je mut a vlastní dáta
// &str — len referencia, nemôžeš meniť obsah
let slice: &str = "ahoj svet";
// slice.push_str("!"); // CHYBA — &str je len pohľad
// char — jeden znak
let c: char = 'a';
let emoji: char = '🦀'; // aj emoji je jeden char
```
**Analógia:**
- `String` je ako vlastný zošit — píšeš doň, pridávaš strany
- `&str` je ako pohľad cez okno na cudzí zošit — vidíš text, ale nemôžeš ho meniť
- `char` je ako jedno písmeno
### Úlohy 1
**1a.** Ktorý typ použiješ v štruktúre? Prečo?
```rust
struct Kniha {
nazov: ???, // String alebo &str?
}
```
**1b.** Čo je zle? Oprav:
```rust
let s: &str = "ahoj";
s.push_str(" svet");
```
**1c.** Aký je typ každej premennej?
```rust
let a = "ahoj";
let b = String::from("ahoj");
let c = 'a';
let d = &b[..];
let e = b.as_str();
```
---
## Kapitola 2: Vytváranie String
Existuje veľa spôsobov ako vytvoriť String:
```rust
// 1. String::from
let s = String::from("ahoj");
// 2. .to_string() — funguje na čomkoľvek s Display
let s = "ahoj".to_string();
let s = 42.to_string(); // "42"
let s = 3.14.to_string(); // "3.14"
let s = true.to_string(); // "true"
// 3. String::new() — prázdny
let s = String::new(); // ""
// 4. format! — ako println!, ale vráti String
let meno = "Anna";
let vek = 25;
let s = format!("{}{} rokov", meno, vek);
// "Anna má 25 rokov"
// 5. .to_owned() — z &str na String
let slice: &str = "ahoj";
let owned: String = slice.to_owned();
// 6. .into() — Rust odvodí konverziu
let s: String = "ahoj".into();
```
**Ktorý použiť:**
- `String::from("...")` alebo `"...".to_string()` — oboje je rovnaké, použi čo sa ti páči
- `format!()` — keď skladáš z viacerých častí
- `String::new()` — keď budeš pridávať postupne
### Úlohy 2
**2a.** Vytvor String z čísla `42`, z boolu `false`, z float `3.14`. Vypíš ich.
**2b.** Máš premenné `meno: &str` a `vek: u32`. Vytvor String vo formáte `"Meno: Anna, Vek: 25"` pomocou `format!`.
**2c.** Vytvor prázdny String a postupne do neho pridaj 3 slová (použi `push_str`). Výsledok vypíš.
---
## Kapitola 3: Konverzie medzi String a &str
```rust
// &str → String (3 spôsoby, všetky robia to isté)
let slice: &str = "ahoj";
let owned1: String = slice.to_string();
let owned2: String = slice.to_owned();
let owned3: String = String::from(slice);
// String → &str (2 spôsoby)
let owned: String = String::from("ahoj");
let slice1: &str = &owned; // automatická dereferencia
let slice2: &str = owned.as_str(); // explicitné
// String → &str v parametroch funkcie — automaticky!
fn prijmi_str(s: &str) {
println!("{}", s);
}
let owned = String::from("ahoj");
prijmi_str(&owned); // String sa automaticky konvertuje na &str
prijmi_str("ahoj"); // &str funguje priamo
```
**Zlaté pravidlo pre funkcie:**
- Parametre píš ako `&str` — prijmeš aj String aj &str
- Návratové hodnoty a polia v štruktúrach píš ako `String` — vlastníš dáta
```rust
// DOBRÉ — parameter &str prijme oboje
fn pozdrav(meno: &str) -> String {
format!("Ahoj, {}!", meno)
}
pozdrav("Anna"); // &str → OK
pozdrav(&String::from("Boris")); // &String → OK
pozdrav(&"Cyril".to_string()); // &String → OK
// ZLÉ — parameter String núti volajúceho robiť konverziu
fn pozdrav_zle(meno: String) -> String {
format!("Ahoj, {}!", meno)
}
// pozdrav_zle("Anna"); // CHYBA — &str nie je String
pozdrav_zle("Anna".to_string()); // musíš konvertovať
```
### Úlohy 3
**3a.** Napíš funkciu `dlzka(text: &str) -> usize` ktorá vráti dĺžku. Zavolaj ju s `&str` aj so `String`.
**3b.** Napíš funkciu `opakuj(text: &str, n: usize) -> String` ktorá vráti text zopakovaný n-krát.
**3c.** Čo je zle? Oprav:
```rust
fn prvych_5(text: String) -> String {
text[..5].to_string()
}
prvych_5("ahoj svet");
```
---
## Kapitola 4: Modifikácia String
String je meniteľný. Tu sú všetky spôsoby ako ho meniť:
### Pridávanie
```rust
let mut s = String::from("ahoj");
// push_str — pridaj &str na koniec
s.push_str(" svet"); // "ahoj svet"
// push — pridaj jeden char
s.push('!'); // "ahoj svet!"
// + operátor — spája String + &str (konzumuje prvý!)
let a = String::from("ahoj");
let b = String::from(" svet");
let c = a + &b; // c = "ahoj svet", a JE ZKONZUMOVANÝ!
// println!("{}", a); // CHYBA — a bolo presunuté do c
// format! — bezpečná konkatenácia (nekonzumuje nič)
let a = String::from("ahoj");
let b = String::from("svet");
let c = format!("{} {}", a, b); // c = "ahoj svet"
println!("{}", a); // OK — a stále existuje
```
### Mazanie
```rust
let mut s = String::from("ahoj svet");
// clear — vymaž všetko
s.clear(); // ""
let mut s = String::from("ahoj svet");
// truncate — skráť na N bajtov
s.truncate(4); // "ahoj"
let mut s = String::from("ahoj");
// pop — odstráň posledný char, vráti Option<char>
let posledny = s.pop(); // Some('j'), s = "aho"
```
### Nahradenie
```rust
let s = String::from("ahoj svet ahoj");
// replace — nahraď všetky výskyty (vráti nový String)
let novy = s.replace("ahoj", "čau");
// "čau svet čau"
// replacen — nahraď len prvých N výskytov
let novy = s.replacen("ahoj", "čau", 1);
// "čau svet ahoj"
```
**Dôležité:** `replace` vracia nový String, nemení pôvodný.
### Úlohy 4
**4a.** Vytvor String `"Rust je super"`. Pridaj na koniec `" jazyk!"`. Pridaj na koniec `'🦀'`. Vypíš výsledok.
**4b.** Máš String `"ahoj_svet_rust"`. Nahraď všetky `_` medzerou. Vypíš.
**4c.** Máš String. Postupne z neho popuj znaky kým nie je prázdny. Každý vypíš.
**4d.** Čo je zle? Oprav (2 spôsoby):
```rust
let a = String::from("ahoj");
let b = String::from(" svet");
let c = a + &b;
println!("{}", a); // chyba!
```
---
## Kapitola 5: char — jeden znak
`char` v Ruste je 4 bajty a reprezentuje jeden Unicode bod (nie bajt!).
```rust
let c: char = 'a';
let emoji: char = '🦀';
let slovak: char = 'ž';
// Kontroly
c.is_alphabetic(); // true — je písmeno?
c.is_numeric(); // false — je číslica?
c.is_alphanumeric(); // true — písmeno alebo číslica?
c.is_uppercase(); // false
c.is_lowercase(); // true
c.is_whitespace(); // false — je medzera/tab/newline?
c.is_ascii(); // true — je ASCII (0-127)?
// Konverzia veľkosť
c.to_uppercase(); // iterátor! nie char (lebo 'ß' → "SS")
c.to_lowercase(); // iterátor!
c.to_ascii_uppercase(); // char — len pre ASCII znaky
c.to_ascii_lowercase(); // char
// char → String
let s: String = c.to_string(); // "a"
let s: String = String::from(c); // "a"
// char → u32 (Unicode code point)
let kod: u32 = c as u32; // 97
// u32 → char (ak je platný Unicode)
let c: Option<char> = char::from_u32(97); // Some('a')
```
**Dôležité o to_uppercase/to_lowercase:**
```rust
// to_uppercase vráti iterátor, nie char!
// Lebo niektoré znaky sa mapujú na viac znakov: ß → SS
let velke: String = 'a'.to_uppercase().collect(); // "A"
let velke: String = 'ß'.to_uppercase().collect(); // "SS"
// Pre jednoduché ASCII použi to_ascii_uppercase — vráti char
let velke: char = 'a'.to_ascii_uppercase(); // 'A'
```
### Úlohy 5
**5a.** Napíš funkciu `je_samohláska(c: char) -> bool` — true pre a, e, i, o, u (aj veľké). Použi `to_ascii_lowercase()` a `matches!` alebo porovnávanie.
**5b.** Napíš funkciu `pocet_cifier(text: &str) -> usize` — koľko znakov v texte sú číslice. Použi `.chars()` a `.is_numeric()`.
**5c.** Napíš funkciu `velkymi(text: &str) -> String` — celý text veľkými písmenami. Použi `.chars()`, `.to_ascii_uppercase()`, `.collect()`.
**5d.** Čo vráti?
```rust
println!("{}", 'A'.is_uppercase());
println!("{}", '5'.is_numeric());
println!("{}", ' '.is_whitespace());
println!("{}", 'ž'.is_ascii());
println!("{}", 'ž'.is_alphabetic());
```
---
## Kapitola 6: Iterácia cez znaky — chars()
String v Ruste je UTF-8. Jeden znak môže byť 14 bajty. Preto nemôžeš indexovať `s[0]` — nevieš koľko bajtov má znak.
```rust
let text = "ahoj";
// chars() — iterátor cez char hodnoty
for c in text.chars() {
println!("{}", c); // a, h, o, j
}
// Počet znakov
let pocet: usize = text.chars().count(); // 4
// Do Vec<char>
let znaky: Vec<char> = text.chars().collect();
// ['a', 'h', 'o', 'j']
// N-tý znak
let druhy: Option<char> = text.chars().nth(1); // Some('h')
// Prvý znak
let prvy: Option<char> = text.chars().next(); // Some('a')
// Posledný znak
let posledny: Option<char> = text.chars().last(); // Some('j')
```
**Pozor na .len() vs .chars().count():**
```rust
let text = "žaba";
println!("{}", text.len()); // 5 (bajtov! 'ž' = 2 bajty)
println!("{}", text.chars().count()); // 4 (znakov)
// Pre čisto ASCII texty sú rovnaké
let text = "ahoj";
println!("{}", text.len()); // 4
println!("{}", text.chars().count()); // 4
```
### chars() s iterátorovými metódami
```rust
let text = "Ahoj Svet 123";
// filter — len písmená
let pismena: String = text.chars().filter(|c| c.is_alphabetic()).collect();
// "AhojSvet"
// map — na veľké
let velke: String = text.chars().map(|c| c.to_ascii_uppercase()).collect();
// "AHOJ SVET 123"
// any — obsahuje číslicu?
let ma_cislo: bool = text.chars().any(|c| c.is_numeric());
// true
// all — sú všetky písmená?
let vsetky_pismena: bool = text.chars().all(|c| c.is_alphabetic());
// false
// enumerate — index + znak
for (i, c) in text.chars().enumerate() {
println!("{}: {}", i, c);
}
// take — prvých N znakov
let prvych_4: String = text.chars().take(4).collect();
// "Ahoj"
// skip — preskoč prvých N znakov
let od_5: String = text.chars().skip(5).collect();
// "Svet 123"
```
### Úlohy 6
**6a.** Napíš funkciu `prevrat(text: &str) -> String` — obráti text. Použi `.chars().rev().collect()`.
**6b.** Napíš funkciu `je_palindrom(text: &str) -> bool` — true ak sa text rovná sám sebe pozadu. Ignoruj veľkosť písmen.
**6c.** Napíš funkciu `censuruj(text: &str, zakazane: &[char]) -> String` — nahraď zakázané znaky hviezdičkou `'*'`. Použi `.chars().map().collect()`.
**6d.** Napíš funkciu `prvych_n_znakov(text: &str, n: usize) -> String`. Použi `.chars().take(n).collect()`.
**6e.** Napíš funkciu `pocet_slov(text: &str) -> usize`. Použi `.split_whitespace().count()`.
---
## Kapitola 7: Rozdeľovanie reťazcov — split, lines, split_whitespace
### split_whitespace — rozdeľ podľa medzier
```rust
let text = " ahoj svet rust ";
let slova: Vec<&str> = text.split_whitespace().collect();
// ["ahoj", "svet", "rust"] — medzery na okrajoch a duplikáty ignorované
```
### split — rozdeľ podľa oddeľovača
```rust
let text = "anna,boris,cyril";
// Podľa znaku
let mena: Vec<&str> = text.split(',').collect();
// ["anna", "boris", "cyril"]
// Podľa reťazca
let text = "ahoj--svet--rust";
let casti: Vec<&str> = text.split("--").collect();
// ["ahoj", "svet", "rust"]
// splitn — maximálne N častí
let text = "a:b:c:d:e";
let casti: Vec<&str> = text.splitn(3, ':').collect();
// ["a", "b", "c:d:e"] — rozdelí len na prvých 2 dvojbodkách
```
### lines — rozdeľ podľa riadkov
```rust
let text = "prvý riadok\ndruhý riadok\ntretí riadok";
for riadok in text.lines() {
println!("{}", riadok);
}
// prvý riadok
// druhý riadok
// tretí riadok
```
### Úlohy 7
**7a.** Máš CSV riadok `"Anna,25,Bratislava"`. Rozdeľ ho na časti. Vypíš meno, vek, mesto.
**7b.** Napíš funkciu `pocet_slov(text: &str) -> usize`. Použi `split_whitespace`.
**7c.** Máš viacriadkový text. Nájdi najdlhší riadok. Použi `.lines()` a `.max_by_key()`.
**7d.** Máš cestu `"/home/user/docs/file.txt"`. Rozdeľ podľa `/`. Vypíš posledný kúsok (filename).
---
## Kapitola 8: Hľadanie v reťazcoch
```rust
let text = "Ahoj svet, ahoj Rust!";
// contains — obsahuje podreťazec?
text.contains("svet"); // true
text.contains("Python"); // false
// starts_with / ends_with
text.starts_with("Ahoj"); // true
text.ends_with("!"); // true
text.ends_with("Rust"); // false (končí na "!")
// find — index prvého výskytu (v bajtoch!)
text.find("svet"); // Some(5)
text.find("Python"); // None
// rfind — od konca
text.rfind("ahoj"); // Some(11)
// matches — iterátor cez všetky výskyty
let pocet = text.matches("ahoj").count(); // 1 (case-sensitive!)
let pocet = text.to_lowercase().matches("ahoj").count(); // 2
```
### Úlohy 8
**8a.** Napíš funkciu `obsahuje_cislicu(text: &str) -> bool` — true ak text obsahuje aspoň jednu číslicu. Použi `.chars().any()`.
**8b.** Napíš funkciu `pocet_vyskytu(text: &str, hladany: &str) -> usize`. Použi `.matches().count()`.
**8c.** Napíš funkciu `zacina_velkym(text: &str) -> bool` — true ak prvý znak je veľké písmeno.
**8d.** Máš vektor reťazcov. Vyfiltruj len tie, ktoré obsahujú slovo "rust" (case-insensitive).
---
## Kapitola 9: Trimovanie a padding
### trim — odstráň biele znaky z okrajov
```rust
let text = " ahoj svet \n";
text.trim(); // "ahoj svet"
text.trim_start(); // "ahoj svet \n"
text.trim_end(); // " ahoj svet"
// trim_matches — odstráň konkrétne znaky
let text = "---ahoj---";
text.trim_matches('-'); // "ahoj"
let text = "***ahoj***svet***";
text.trim_matches('*'); // "ahoj***svet" (len okraje!)
```
### Padding / zarovnanie (nie metóda, ale format!)
```rust
let meno = "Anna";
// Zarovnanie vpravo (šírka 10)
let s = format!("{:>10}", meno); // " Anna"
// Zarovnanie vľavo
let s = format!("{:<10}", meno); // "Anna "
// Na stred
let s = format!("{:^10}", meno); // " Anna "
// S vlastným znakom
let s = format!("{:*>10}", meno); // "******Anna"
let s = format!("{:-^10}", meno); // "---Anna---"
```
### Úlohy 9
**9a.** Máš vstup od používateľa `" ahoj \n"`. Očisti ho trimom. Potom over, či je "ahoj".
**9b.** Máš vektor mien rôznej dĺžky. Vypíš ich zarovnané vpravo na šírku 15 znakov:
```
Anna
Boris
Cyril
```
**9c.** Napíš funkciu `ocisti(text: &str) -> String` — trim + nahraď viacnásobné medzery jednou. Hint: `split_whitespace().collect::<Vec<&str>>().join(" ")`.
---
## Kapitola 10: Slicing — &str z časti String
Môžeš vziať kúsok reťazca ako &str. Ale **pozor** — indexy sú v bajtoch, nie znakoch!
```rust
let text = "ahoj svet";
// Slice podľa rozsahu bajtov
let slice: &str = &text[0..4]; // "ahoj"
let slice: &str = &text[5..]; // "svet"
let slice: &str = &text[..4]; // "ahoj"
// NEBEZPEČNÉ pre non-ASCII!
let text = "žaba";
// &text[0..1] // PANIC! 'ž' je 2 bajty, toto by rozdelilo uprostred znaku!
// &text[0..2] // OK — "ž"
```
**Bezpečný spôsob pre ľubovoľný text:**
```rust
// Použi chars() namiesto slicingu
fn prvy_znak(text: &str) -> Option<char> {
text.chars().next()
}
fn prvych_n(text: &str, n: usize) -> String {
text.chars().take(n).collect()
}
fn od_znaku_n(text: &str, n: usize) -> String {
text.chars().skip(n).collect()
}
fn podretazec(text: &str, od: usize, do_: usize) -> String {
text.chars().skip(od).take(do_ - od).collect()
}
```
**Kedy je slice bezpečný:**
- Ak vieš, že text je čistý ASCII (anglická abeceda, čísla)
- Ak index získaš z `.find()` (ten vracia bajtový index)
```rust
let text = "ahoj svet rust";
if let Some(pos) = text.find("svet") {
let od_svet: &str = &text[pos..]; // "svet rust" — bezpečné, find vrátil správny index
}
```
### Úlohy 10
**10a.** Máš ASCII text `"Hello World"`. Vyber prvých 5 znakov slicingom. Vyber posledných 5.
**10b.** Napíš funkciu `bezpecne_prvych_n(text: &str, n: usize) -> String` — vráti prvých N znakov bez paniki (aj pre non-ASCII).
**10c.** Máš text. Nájdi pozíciu prvej medzery pomocou `.find(' ')`. Ak existuje, vráť prvé slovo ako &str slice:
```rust
fn prve_slovo(text: &str) -> &str
```
**10d.** Prečo toto panikne? Ako to opravíš?
```rust
let text = "café";
let slice = &text[0..4];
```
---
## Kapitola 11: Parsovanie — z reťazca na iné typy
```rust
// &str / String → číslo (.parse())
let cislo: i32 = "42".parse().unwrap(); // 42
let cislo: f64 = "3.14".parse().unwrap(); // 3.14
let cislo: Result<i32, _> = "nie_cislo".parse(); // Err(...)
// Bezpečné parsovanie
let vstup = "42";
match vstup.parse::<i32>() {
Ok(n) => println!("Číslo: {}", n),
Err(_) => println!("Nie je číslo"),
}
// Alebo s .ok()
let cislo: Option<i32> = vstup.parse().ok();
// číslo → String
let text: String = 42.to_string();
let text: String = format!("{}", 42);
// bool
let b: bool = "true".parse().unwrap(); // true
let b: bool = "false".parse().unwrap(); // false
```
### Úlohy 11
**11a.** Napíš funkciu `parsuj_cisla(text: &str) -> Vec<i32>` — text obsahuje čísla oddelené čiarkami (`"1,2,3,4,5"`). Rozdeľ a sparsuj. Ignoruj neplatné hodnoty.
**11b.** Napíš funkciu `parsuj_csv_riadok(riadok: &str) -> Option<(String, u32, f64)>`:
Vstup: `"Anna,25,1.5"``Some(("Anna".to_string(), 25, 1.5))`
Ak parsovanie zlyhá → None.
**11c.** Máš vstup od používateľa (String). Trim ho, parsuj na u32. Ak sa nepodarí, vyskúšaj f64. Ak ani to, vráť chybu.
---
## Kapitola 12: Pokročilé operácie
### repeat — opakuj reťazec
```rust
let s = "ha".repeat(3); // "hahaha"
let s = "-".repeat(20); // "--------------------"
```
### to_lowercase / to_uppercase
```rust
let text = "Ahoj Svet";
let male: String = text.to_lowercase(); // "ahoj svet"
let velke: String = text.to_uppercase(); // "AHOJ SVET"
// Porovnanie case-insensitive
fn rovnake_ci(a: &str, b: &str) -> bool {
a.to_lowercase() == b.to_lowercase()
}
rovnake_ci("Ahoj", "AHOJ"); // true
```
### char_indices — indexy aj znaky
```rust
let text = "žaba";
// char_indices dáva (bajtový_index, char)
for (i, c) in text.char_indices() {
println!("bajt {}: {}", i, c);
}
// bajt 0: ž
// bajt 2: a (ž zabrala 2 bajty!)
// bajt 3: b
// bajt 4: a
```
### Collect medzi typmi
```rust
// Vec<char> → String
let znaky = vec!['a', 'h', 'o', 'j'];
let text: String = znaky.into_iter().collect();
// "ahoj"
// Vec<&str> → String (s oddeľovačom)
let slova = vec!["ahoj", "svet"];
let text: String = slova.join(" ");
// "ahoj svet"
// Vec<String> → String (s oddeľovačom)
let slova: Vec<String> = vec!["ahoj".into(), "svet".into()];
let text: String = slova.join(", ");
// "ahoj, svet"
// Iterátor &str → String
let text: String = ["ahoj", "svet"].iter().copied().collect::<Vec<&str>>().join(" ");
// S itertools je jednoduchšie:
// use itertools::Itertools;
// let text = ["ahoj", "svet"].iter().join(" ");
```
### Úlohy 12
**12a.** Napíš funkciu `oddelovac(sirka: usize) -> String` — vráti riadok pomlčiek danej šírky.
**12b.** Napíš funkciu `kapitalizuj(text: &str) -> String` — prvé písmeno veľké, zvyšok malé. Hint: `.chars().next()` pre prvé, `.chars().skip(1)` pre zvyšok.
**12c.** Napíš funkciu `snake_to_camel(text: &str) -> String` — preveď `"ahoj_svet_rust"` na `"AhojSvetRust"`. Hint: split podľa `_`, každé slovo kapitalizuj, spoj.
**12d.** Napíš funkciu `je_platny_email(text: &str) -> bool`:
- Obsahuje presne jednu `@`
- Pred `@` je aspoň 1 znak
- Za `@` je aspoň 3 znaky
- Za `@` obsahuje `.`
---
## Kapitola 13: Vzory zo skúšky
### Vzor 1: Display pre štruktúru/enum
```rust
use std::fmt;
enum Stav { Novy, Aktivny, Dokonceny }
impl fmt::Display for Stav {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Stav::Novy => write!(f, "Nový"),
Stav::Aktivny => write!(f, "Aktívny"),
Stav::Dokonceny => write!(f, "Dokončený"),
}
}
}
struct Uloha { nazov: String, stav: Stav }
impl fmt::Display for Uloha {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} [{}]", self.nazov, self.stav)
}
}
// Výstup: "Upratovanie [Nový]"
```
### Vzor 2: Obesenec — zobrazenie skrytého slova
```rust
fn zobraz_slovo(slovo: &str, uhadnute: &HashSet<char>) -> String {
slovo.chars().map(|c| {
if uhadnute.contains(&c) { c } else { '_' }
}).collect()
}
// zobraz_slovo("ahoj", &{'a', 'o'}) → "a_o_"
```
### Vzor 3: Parsovanie vstupu od používateľa
```rust
fn nacitaj_cislo() -> Option<u32> {
let mut vstup = String::new();
std::io::stdin().read_line(&mut vstup).ok()?;
vstup.trim().parse().ok()
}
```
### Vzor 4: CSV / JSON parsovanie
```rust
fn parsuj_knihu(riadok: &str) -> Option<Kniha> {
let casti: Vec<&str> = riadok.split(',').collect();
if casti.len() != 3 { return None; }
let rok: u16 = casti[2].trim().parse().ok()?;
Some(Kniha {
nazov: casti[0].trim().to_string(),
autor: casti[1].trim().to_string(),
rok,
})
}
```
### Úlohy 13
**13a.** Implementuj `Display` pre enum `Priorita { Nizka, Stredna, Vysoka }` — výstup: "Nízka", "Stredná", "Vysoká".
**13b.** Implementuj `Display` pre štruktúru `Zamestnanec { meno: String, pozicia: String, plat: u32 }` vo formáte `"Anna (Programátor) - 3000€"`.
**13c.** Napíš funkciu, ktorá načíta riadok z stdin, trimne ho a vráti. Ak je prázdny po trime, vráti None:
```rust
fn nacitaj_riadok() -> Option<String>
```
**13d.** Napíš funkciu, ktorá sparsuje text `"meno:vek:mesto"` na tuple:
```rust
fn parsuj(text: &str) -> Option<(String, u32, String)>
```
---
## Prehľad konverzií
```
&str ──.to_string()──→ String
&str ──.to_owned()───→ String
&str ──String::from()─→ String
String ──&──────────→ &str
String ──.as_str()──→ &str
char ──.to_string()─→ String
char ──String::from()→ String
&str ──.chars()─────→ Iterator<char>
String ──.chars()───→ Iterator<char>
Iterator<char> ──.collect()──→ String
Vec<char> ──.iter().collect()→ String
&str ──.parse::<T>()──→ Result<T, Error>
T ──.to_string()───────→ String (ak T: Display)
&str ──.as_bytes()────→ &[u8]
```

1031
priprava/rust_priprava9.md Normal file

File diff suppressed because it is too large Load Diff

123
riesenie1/Cargo.lock generated Normal file
View File

@@ -0,0 +1,123 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
[[package]]
name = "riesenie1"
version = "0.1.0"
dependencies = [
"itertools",
"serde",
"serde_json",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

9
riesenie1/Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "riesenie1"
version = "0.1.0"
edition = "2024"
[dependencies]
itertools = "0.14.0"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"

118
riesenie1/src/lib.rs Normal file
View File

@@ -0,0 +1,118 @@
// src/lib.rs
use std::fmt;
use std::fs::File;
use std::fs;
use std::collections::HashMap;
use std::io::Write;
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Kniha {
autori: Vec<String>,
nazov: String,
vydavatelstvo: String,
zaner: String,
pocet_stran: usize,
isbn: String,
rok_vydania: String,
pozicana: bool,
stav_knihy: Stav,
}
impl fmt::Display for Kniha {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({:#?}, {}, {}, {}, {}, {}, {}, {}, {:#?})",
self.autori,
self.nazov,
self.vydavatelstvo,
self.zaner,
self.pocet_stran,
self.isbn,
self.rok_vydania,
self.pozicana,
self.stav_knihy,
)
}
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize, PartialEq)]
pub enum Stav {
#[default]
Nova,
Pouzivana,
Poskodena,
Vyradena
}
impl fmt::Display for Stav {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Stav::Nova => write!(f, "Nova"),
Stav::Pouzivana => write!(f, "Pouzivana"),
Stav::Poskodena => write!(f, "Poskodena"),
Stav::Vyradena => write!(f, "Vyradena"),
}
}
}
#[derive(Default, serde::Serialize, serde::Deserialize)]
pub struct Kniznica {
knihy: Vec<Kniha>
}
impl Kniznica {
pub fn nacitaj_zo_suboru(r: &std::path::PathBuf) -> Option<Kniznica> {
let d: String = std::fs::read_to_string(r).ok()?;
let r: Option<Kniznica> = serde_json::from_str(&d).ok()?;
r
}
pub fn uloz_do_suboru(&self, z: &std::path::PathBuf) -> bool {
let Ok(f) = File::create(z) else{
return false;
};
serde_json::to_writer_pretty(f, &self).is_ok()
}
pub fn pridaj_knihu(&mut self, k: Kniha) -> Result<(), ()> {
if self.knihy.iter().map(|x| x.isbn.clone()).any(|y| y == k.isbn) {
return Err(());
}
self.knihy.push(k);
Ok(())
}
pub fn odstran_knihu(&mut self, r: &str) -> Result<Kniha, ()> {
let Some(k) = self.knihy.iter().position(|x| x.isbn == r) else {
return Err(());
};
let t = self.knihy.remove(k);
Ok(t)
}
pub fn daj_knihu_podla_isbn(&self, i: &str) -> Option<&Kniha> {
self.knihy.iter().find(|x| x.isbn == i)
}
pub fn daj_knihy_autora(&self, a: &str) -> Vec<&Kniha> {
self.knihy.iter().filter(|y| y.autori.contains(&a.to_string())).collect()
}
pub fn daj_knihy_podla_stavu(&self, s: Stav) -> Vec<&Kniha> {
self.knihy.iter().filter(|x| x.stav_knihy == s).collect()
}
pub fn vypis_vydavatelstva_a_pocet_knih(&self) {
let mut h: HashMap<String, usize> = HashMap::new();
for x in self.knihy.iter() {
h.entry(x.vydavatelstvo.clone()).and_modify(|x| *x += 1).or_insert(1);
}
// self.knihy.iter().counts();
for y in h.iter() {
println!("{}: {} knih", y.0, y.1)
}
}
pub fn vypis_knihy_daneho_zanru(&self, zk: &str) {
self.knihy.iter().filter(|&x| x.zaner == zk).map(|x| println!("{:#?}", x));
}
}

3
riesenie1/src/main.rs Normal file
View File

@@ -0,0 +1,3 @@
fn main() {
}