Podľa prednášky implementujte a v krátkom programe použite:
Pri použití koprogramu nezabudnite na „inicializáciu“ pomocou volania funkcie next()
(alebo metódy send(None)
nad vytvoreným objektom rozšíreného generátora). Ak ste sa oboznámili s dekorátormi v Pythone, spokojne môžete implementovať aj dekorátor, ktorý vám uľahčí vytváranie koprogramov. Dekorátor by mal:
next()
s argumentom vytvoreného objektu koprogramu, Napíšte program, ktorý v nejakom textovom súbore (súbor môže byť napevno daný v programe) nájde počet výskytov nejakého slova (slovo môže byť napevno dané v programe). Tento program nech je implementovaný pomocou koprogramov rozšírených generátorov.
Pri riešení môžeme rozdeliť problém do troch častí (aby sme pre edukatívne účely využili čo najviac koprogramov 😉):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
def cat(f, next_fnc): """Obycajna funkcia, cita subor riadok po riadku. Kazdy riadok posle na spracovanie koprogramu next_fnc(). Po skonceni citania riadkov vstupneho suboru ukonci cinnost generatora, ktoremu posiela udaje. Funkcia close() generatoru posle vynimku GeneratorExit. """ for line in f: next_fnc.send(line) next_fnc.close() def grep(substring, next_fnc): """Koprogram, ktory caka na vstupny riadok textu zo suboru Akonahle ziska riadok, dalsiemu koprogramu posiela pocet vyskytov podretazca `substring` v danom riadku. Po prijati vynimky GeneratorExit tuto vynimku preposle koprogramu, ktory spracuva pocet vyskytov podretazca. """ try: while True: line = (yield) next_fnc.send(line.count(substring)) except GeneratorExit: next_fnc.close() def wc(substring): """Koprogram, ktory caka na pocet vyskytov podretazca v riadku. Akonahle dostane pocet, pripocitava ho k celkovemu vysledku. Po obdrzani vynimky GeneratorExit cinnost koprogramu konci vypisom vysledku na obrazovku. """ n = 0 try: while True: n += (yield) except GeneratorExit: print(substring, n, flush=True) |
Ešte by bolo dobré napísať kód, ktorý tieto funkcie „oživí“. Príklad jednoduchého použitia môže vyzerať nasledovne:
1 2 3 4 5 6 7 8 9 10 |
if __name__ == "__main__": f = open("05.smoker_scoreboard_optim.py", "r") substring = "RES_T" w = wc(substring) next(w) g = grep(substring, w) next(g) cat(f, g) |
Je snáď dostatočne zrejmé, že vo vašich programoch je potrebné zmeniť obsahy premenných ‚f‘ a ‚substring‘ tak, aby ste dosiahli želané výsledky.
V doteraz vytvorenom kóde nie je veľmi vidno víťazstvo asynchrónneho prístupu. Máme jednu funkciu cat()
, ktorá načítaný vstup odovzdá jedinej čakajúcej funkcii grep()
, ktorá zas posiela výsledok svojho spracovanie jedinej čakajúcej funkcii wc()
. Aby to bolo zaujímavejšie, skúsme tento jednoduchý program rozšíriť o možnosť spracovania (vyhľadania počtu výskytov) viacerých slov!
Pre každé slovo, ktorého výskyt v súbore nás zaujíma, vytvoríme práve jednu dvojicu inštancií funkcií grep()
a wc()
. Potom vložíme akúsi ‚medzivrstvu‘ za funkciu cat()
; úlohou tejto medzivrstvy bude doručiť každej čakajúcej funkcii grep()
riadok textu, ktorý medzivrstva obdrží od funkcie cat()
.
V nasledovnom kóde predpokladáme, že implementácia funkcií cat()
, grep()
a wc()
sa nezmenila!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
def dispatch(greps): """Koprogram, ktoreho ulohou je distribuovat riadok ziskany z funkcie cat() medzi vsetky koprogramy, ktore ma ulozene vo vstupnom zozname `greps`. """ try: while True: line = (yield) for grep in greps: grep.send(line) except GeneratorExit: for grep in greps: grep.close() # upravime aj priklad pouzitia if __name__ == "__main__": f = open("05.smoker_scoreboard_optim.py", "r") substrings = ["RES_T", "pushers", "agents", "shared"] greps = [] for substring in substrings: w = wc(substring) next(w) g = grep(substring, w) next(g) greps.append(g) d = dispatch(greps) next(d) cat(f, d) |
Dosiahli sme úžasnú vec: bezo zmeny funkcií grep()
, wc()
a cat()
sme pridaním ďalšieho koprogramu rozšírili program o možnosť hľadania počtu výskytov z jedného na takmer ľubovoľný počet slov.
Na záver tohto bloku dodávame, že Python poskytuje možnosť využívať ‚pravé‘ koprogramy pomocou async def
a await
až od verzie 3.5. Pre jednoduchosť sme sa však rozhodli načrtnúť možnosti asynchrónneho programovania pomocou rozšírených generátorov, prítomných v jazyku Python od verzie 2.5.
Asynchrónne programovanie je mocný nástroj, avšak ako všetky nástroje, aj tento treba používať s rozvahou a iba tam, kde je to vhodné: v aplikáciách, ktoré veľmi veľa času strávia pri čakaní na vstupno-výstupné operácie. Pri vývoji asynchrónnych programov je však potrebné počítať s vyššou zložitosťou programového kódu. Takmer nič však nie je vo svete zadarmo; v tomto prípade platí, že zvýšenie výkonu získame za cenu vyššej zložitosti kódu.
Pri písaní asynchrónnych funkcií je potrebné dávať veľký pozor na to, aké funkcie v tele koprogramu používame. Treba sa vyhýbať takým, ktoré vedú k blokovaniu, pretože tým sa stráca zmysel asynchrónneho kódu. Plne asynchrónna aplikácia si vyžaduje napísanie vlastného plánovača na vyvolávanie behu koprogramov, pretože operačný systém nedokáže plánovať na beh koprogramy aplikácie tak, ako je to v prípade procesov a/alebo vlákien. Takýto plánovač pridáva ovšem ďalšiu zložitosť kódu. Čítanie programového kódu asynchrónnej aplikácie je poriadnou výzvou, ako ste si mali možnosť všimnúť už na triviálnom programčeku tohto cvičenia.
Napíšte aplikáciu, ktorá bude využívať N (N>2) koprogramov (pomocou rozšírených generátorov) a na ich striedanie použite vlastný plánovač. Na riešenie nepoužite programový kód z odporúčanej literatúry.