A feladat tehát az, hogy ha van pl. három karakterláncunk ‘ABC’, ‘DEF’, ‘GHI’, akkor az iterátorból a következő sorozat legyen kinyerhető: ‘A’, ‘D’, ‘G’, ‘B’, ‘E’, ‘H’, ‘C’, ‘F’, ‘I’.
A feladat több módon is megoldható. Ezekben a közös elv, hogy először is előállítjuk az iterálható objektumok iterátorait az iter() beépített függvénnyel, hogy utána ezekből tudjuk egyenként kikérni az értékeiket. Az így kapott iterátorokat eltároljuk olyan sorrendben, ahogy a bemenő sorozatok vannak, hogy sorban egymás után periodikusan fel tudjuk őket kínálni a váltakozó elemkikéréshez. Létrehozzuk a ciklikus elemkinyerés és értékvisszaadás szerkezetét. A megvalósítási módok valójában ez utóbbi részben térnek el.
Az alábbiakban néhány lehetséges implementációt mutatunk, amelyekhez fűzött kommentek segítik a megértést. Az első egy iterátorosztályt definiál, a következő kettő egy-egy generátorfüggvényt, és végül az utolsó egy olyan függvényt, amely egy iterátorral tér vissza. Látható, hogy ez utóbbi a legegyszerűbb szerkezetű megvalósítás.
|
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 |
from itertools import count class ElementInterleavingIterator: """A példány olyan iterátor, amely a megadott iterálható objektumok elemeit felváltva, sorban egymás után szolgáltatja. Pl. 'ABC', 'DEF' -> 'A','D','B','E','C','F' """ def __init__(self, *iterable_objects): # Előállítjuk és eltároljuk az iterálható objektumok iterátorait. self.iterators = [iter(x) for x in iterable_objects] # Eltároljuk az iterálható objektumok számát. self.n = len(iterable_objects) # Létrehozunk egy 0 kezdőértékű és 1 növekményű számlálóobjektumot, amely # folyamatosan szolgáltat egész számokat. self.counter = count() def __next__(self): # Indexek periódikus előállítása moduló osztással a számlálóobjektum és # az iterators száma alapján. i = next(self.counter) % self.n # Kikérjük az aktuális indexű iterátorból a következő elemet. return next(self.iterators[i]) def __iter__(self): return self # TESZT print(''.join(ElementInterleavingIterator('ph', 'yo', 'tn'))) # Eredmény: python |
|
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 |
from itertools import count, cycle def interleaver_gen1(*iterable_objects): iterators = [iter(x) for x in iterable_objects] n = len(iterable_objects) counter = count() while True: try: # Addig szolgáltatjuk ki az elemeket, amíg az egyik iterátor ki nem merül. # Ekkor kilépünk a ciklusból. yield next(iterators[next(counter) % n]) except StopIteration: break def interleaver_gen2(*iterable_objects): iterators = [iter(x) for x in iterable_objects] n = len(iterable_objects) while True: for itr in iterators: try: yield next(itr) # Ha ez egyik iterátor kimerül, kilépünk a függvényből és így mindkét # ciklusokból is. except StopIteration: return def interleaver_fn(*iterable_objects): # Az iterálható objektumok sorozat minden egy elemének előállítjuk az iterátorát # egy sorozatban. Az iterátorsorozat ciklikusan ismétlődő sorozata aktuális # elemének (ami egy iterátor) kikérjük a soron következő értékét. return map(next, cycle(map(iter, iterable_objects))) # TESZT print(''.join(interleaver_gen1('iro', 'tár', 'et'))) # Eredmény: iterátor print(''.join(interleaver_gen2('get', 'ero', 'nár'))) # Eredmény: generátor print(''.join(interleaver_fn('itsd', 'to u', 'eoml', 'rlo'))) # Eredmény: itertools modul |
A változatok többségében a szabványos könyvtár itertools moduljának lehetőségeit használtuk ki. Általánosságban is igaz, hogy ha iterátorok előállítása a feladat, akkor érdemes az itertools modul kínálatát megvizsgálni, mert használatával jelentősen leegyszerűsödhet a megoldást nyújtó kód megírása.
A Python tudásépítés lépésről lépésre című e-könyvben az itertools modulról részletesen a „Speciális iterátorok” fejezetben olvashatunk számos példával segítve a megértést és használatot.