Hogyan daraboljunk fel egy sorozatot egyenlő méretű részekre a további feldolgozáshoz? – 1. rész

Olykor szükség lehet arra, hogy egy sorozatot adott hosszú darabokként, egymást követő részsorozatokként dolgozzuk fel. Hétköznapi példa lehet, hogy egy gyári futószalagról folyamatosan jövő termékeket, mondjuk italokat hatosával gyűjtik egy dobozban, amit a boltokba kiszállítanak. Egy másik eset lehet, hogy a reptéren várakozó és haza igyekvő utasokat érkezési sorrendben reptéri minibuszokkal kilencesével tudják elszállítani, tehát egyszerre mindig kilenc embert vesznek ki a sorból. Az ilyen, darabonkénti elszállítások következtében a várakozók sorának végén lehet, hogy kilencnél kevesebb fő marad, de természetesen, akkor ez alkot majd egy csoportot, és ezt is elviszi a minibusz.

Vegyük észre, hogy az említett két példában bár mindkét esetben az eredeti sorozatot azonos hosszúságú (6 és 9) részsorozatokra daraboltuk a további feldolgozáshoz, van egy lényeges eltérés: a futószalag esetében, mivel folyamatos a palackok előállítása, nem tudjuk előre, hogy mennyi az összes palack száma, és az egyes palackok azonosítására előre hozzárendelt indexet sem tudunk használni, legfeljebb egy adott pillanattól tudjuk számlálni az ezután legördülő italokat. Más lenne a helyzet, ha az italok egy raktárban lennének polcokon tárolva, mert ekkor tudnánk a raktárkészlet mennyiségét és az egyes italokat akár a polcokon levő sorszámok alapján is tudnánk azonosítani. A reptéren várakozók esete inkább ehhez hasonlít, hiszen meg tudjuk mondani, hogy hányan érkeztek meg a járattal, azaz hányan várakoznak hazaszállításra, és sorrendbe is állíthatjuk az utasokat például a kicsekkolási sorrend alapján.

Lemodellezve a két példát, azt mondhatjuk, hogy az utasok esetében egy indexelhető sorozat típusú konténer elemeit akarjuk 9 elemet tartalmazó részsorozatkora bontani. Az italgyártósor esetében viszont nem beszélhetünk konténerről, hanem csak egy iterálható objektumról, amelynek elemeit – az iterátorának előállítása után – ki tudjuk kérni sorban egymás után, és így kell előállítani a 6 elemet tartalmazó részsorozatokat, amelyeket már gyűjthetünk egy sorozat típusú konténerbe (list vagy tuple).

A modelljeink ismeretében írjunk először egy olyan függvényt, amely egy adott sorozat típusú konténer elemeit megadható hosszúságú darabokra bontja, és ezeket a részsorozatokat adja vissza.

Ha véges elemszámú és indexelhető konténer elemeit mint sorozatot kell darabolni, akkor a szeletképzéssel juthatunk talán legegyszerűbben a megoldáshoz. Mivel tudjuk a sorozat n hosszát, ezért egymás után vesszük az indexeket, és n széles szeleteket képezünk.

A szeleteket egy listában gyűjtjük, és a végén ezt a listát adja vissza a függvény. Ezt mutatja az alábbi függvénydefiníció.

Minthogy egy listát építünk a szeletekből, ezért ilyenkor érdemes megvizsgálni, hogy nem alkalmazhatunk-e egy listaépítő kifejezést (list comprehension), mert azzal kicsit egyszerűbb lesz a kód. Esetünkben de igen, alkalmazhatjuk ezt a fajta megvalósítást, amely így néz ki:

Hosszú sorozat esetén nem biztos, hogy célszerű, ha egy nagyméretű listát adunk vissza, hanem az is megfelelő, ha egy olyan iterátort, amely minden egyes kérésre egy sorozatdarabot szolgáltat ki. Ezt könnyen megtehetjük, ha az előző függvény listaépítő kifejezésében az elején és végén a szögletes zárójeleket gömbölyű zárójelekre cseréljük, mert ekkor egy generátorkifejezést kapunk, amely épp a célunknak megfelelő. Ezt a változatot láthatjuk alább.

Ugyanezt elérhetjük úgy is, hogy generátorfüggvénnyé alakítjuk az előző függvényt azzal, hogy a return helyett a yield utasítással adjuk ki a visszatérési értéket. Pontosabban nem a yield, hanem a yield from kell most, mert a yield magát a generátorkifejezést adná vissza egy next() vagy __next__() kérésre. Ezzel szemben a yield from az utána álló iterálható objektumból kéri ki sorban az elemeket. Ezt, valamint a négy függvény tesztkódját és eredményeit mutatja a következő kód.

A következő bejegyzésben azt fogjuk megnézni, hogy hogyan kell részsorozatokat kiadó függvényeket definiálni akkor, ha az argumentum nem csak konténer, hanem tetszőleges iterálható objektum lehet.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.