C++ vyfiltrovanie položiek iterátoru a vrátenie ďalšieho iterátoru

Mám funkciu filesystem::directory_iterator ktorá vracia všetky filesystem entries vrátane files, directories, symlinky a rôzne zariadenia etc.

a ja by som si chcel spraviť 2 helper funkcie:

jedna sa bude volať directory::contained_files vracajúca len súbory vo folderi a druhá directory::contained_directories  vracajúca len foldre.

Ale chcem aby to vracalo zase iterátor (nie kontainer ale iterátor)

viem že v directory_entry má funkcie ktoré vrátia boolean pri is_directory alebo is_regular_file ale ako vrátiť znovu iterátor? V iných jazykoch by som tam hodil cyklus a pri generátoroch a urobil yield alebo použil Seq.filter či LINQ where Ale C++ má korutiny až od verize 20 a tá ešte neni hotová. V mojej preview verzii C++ 20 zatiaľ neni ani typ generator<item_name> ani co_yield takže ako to by som to mal riešiť bez korutín?


Podľa toho čo vidím v zdrojáku directory_iteratora tak by som si asi musel napísať vlastný iterátor.

Pro zajímavost, v Rustu se to dělá idiomaticky bez korutin i bez implementace vlastního iterátoru. Když už mám iterátor, a chci z něj jen vyfiltrovat některé položky, použiju prostě metodu filter, která vrací jiný iterátor. Ten má v sobě zabalený ten původní a predikát, při iteraci přeskakuje nevyhovující.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c93f0ee782a56a5e475f6e26c95229f3

fn filter_files(iter: impl Iterator<Item=String>) -> impl Iterator<Item=String> {
    iter.filter(|item| item.starts_with("Soubor"))
}

fn main() {
    let v = vec!["Soubor XXX".to_string(), "Adresar YYY".to_string()];
    for s in filter_files(v.into_iter()) {
        println!("{}", s)
    }
}


vypíše Soubor XXX.

« Poslední změna: 12. 01. 2021, 16:05:15 od Baloun »

Pro zajímavost, v Rustu se to dělá idiomaticky bez korutin i bez implementace vlastního iterátoru. Když už mám iterátor, a chci z něj jen vyfiltrovat některé položky, použiju prostě metodu filter, která vrací jiný iterátor. Ten má v sobě zabalený ten původní a predikát, při iteraci přeskakuje nevyhovující.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c93f0ee782a56a5e475f6e26c95229f3

fn filter_files(iter: impl Iterator<Item=String>) -> impl Iterator<Item=String> {
    iter.filter(|item| item.starts_with("Soubor"))
}

fn main() {
    let v = vec!["Soubor XXX".to_string(), "Adresar YYY".to_string()];
    for s in filter_files(v.into_iter()) {
        println!("{}", s)
    }
}


vypíše Soubor XXX.

Hej implementovať vlastný iterátor nemusíte, lebo ho už pred vami implemetovali programátri rustovského ekosystému. filter vracia špeciálny iterátor s názvom Filter a ten je natoľko obecný (keďže obsahuje predikát) že sa dá použiť všade kde sa niečo filtruje, aspoň teda ak správne čítam zdroják:

url=https://doc.rust-lang.org/nightly/src/core/iter/adapters/filter.rs.html

Podobne to funguje aj v LINQ (v C# a F#) cca 15 rokov: Seq.filter a (F#)Enumerable.Where (C#) vracaju WhereEnumerableIterator a WhereIterator. Tu je zdroják... funkcia filter Where je hneď na začiatku:

Edit: nie filter ale Where pletie sa mi to lebo Seq.filter je vlastne alias pre Enumerable.Where a Seq.map je niečo ako alias pre Enumerable.Select

https://referencesource.microsoft.com/#system.core/system/linq/Enumerable.cs

Mimochodom aj v java to má podobne (odkedy tam pridali streamy)

Aj STL či boost má funkcie z rovnakého súdka napr std::copy_if je náhrada za filter a std::transform náhrada za map etc aj keď iterárátormi sa tam pracuje o niečo krkolomnejšie používajú sa tam insertery a podobne, ale zase sa to dá aj viac customizovať. Ja som pre C++ hľadal niečo viac sexy, čo funguje podobne ako váš príklad z Rustu. Akurát C++ je veľmi konzervatívny jazyk, ktorý pridáva nové vlastnosti pomaly a to až vtedy keď su poriadne odstestované naprieč ostatnými jazykmi. Nepozerá až tak veľmi na sexy zápis, ale skôr performance. A keď sa tam má dostať nejaká nová vlastnosť tak až vtedy keď sa navrhne nejaká veľmi efektívna a dokonale customizovateľná univerzálna implemetácia.

Ale inak ďakujem za vysvetlenie :) Rust je jeden z jazykov, ktorý má do budúcna potenciál možno raz aj nahradiť C/C++
« Poslední změna: 12. 01. 2021, 17:51:10 od fortran1986 »



Pro zajímavost, v Rustu se to dělá idiomaticky bez korutin i bez implementace vlastního iterátoru. Když už mám iterátor, a chci z něj jen vyfiltrovat některé položky, použiju prostě metodu filter, která vrací jiný iterátor. Ten má v sobě zabalený ten původní a predikát, při iteraci přeskakuje nevyhovující.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c93f0ee782a56a5e475f6e26c95229f3

fn filter_files(iter: impl Iterator<Item=String>) -> impl Iterator<Item=String> {
    iter.filter(|item| item.starts_with("Soubor"))
}

fn main() {
    let v = vec!["Soubor XXX".to_string(), "Adresar YYY".to_string()];
    for s in filter_files(v.into_iter()) {
        println!("{}", s)
    }
}


vypíše Soubor XXX.

Hej implementovať vlastný iterátor nemusíte, lebo ho už pred vami implemetovali programátri rustovského ekosystému. filter vracia špeciálny iterátor s názvom Filter ...

Jasně, vždyť přesně to můj příklad dělá :-) filter_files vrací Filter, akorát že ten Filter je tu maskovaný pod typem impl Iterator<Item=...>, abych se nemusel s tím typem vypisovat, a taky v budoucnu umožnil eventuelní změnu implementace bez narušení kompatibility typů funkce..

Pro zajímavost, v Rustu se to dělá idiomaticky bez korutin i bez implementace vlastního iterátoru. Když už mám iterátor, a chci z něj jen vyfiltrovat některé položky, použiju prostě metodu filter, která vrací jiný iterátor. Ten má v sobě zabalený ten původní a predikát, při iteraci přeskakuje nevyhovující.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c93f0ee782a56a5e475f6e26c95229f3

fn filter_files(iter: impl Iterator<Item=String>) -> impl Iterator<Item=String> {
    iter.filter(|item| item.starts_with("Soubor"))
}

fn main() {
    let v = vec!["Soubor XXX".to_string(), "Adresar YYY".to_string()];
    for s in filter_files(v.into_iter()) {
        println!("{}", s)
    }
}


vypíše Soubor XXX.

Hej implementovať vlastný iterátor nemusíte, lebo ho už pred vami implemetovali programátri rustovského ekosystému. filter vracia špeciálny iterátor s názvom Filter ...

Jasně, vždyť přesně to můj příklad dělá :-) filter_files vrací Filter, akorát že ten Filter je tu maskovaný pod typem impl Iterator<Item=...>, abych se nemusel s tím typem vypisovat, a taky v budoucnu umožnil eventuelní změnu implementace bez narušení kompatibility typů funkce..

Jasné chápem