Hogyan „forgassuk” egy sorozat elemeit?

Az előző bejegyzésben, a naptár alkalmazás igényeink szerinti működéséhez szükség volt egy lista elemeinek forgatására. Egy sorozat típusú konténer elemeinek n-szeres forgatásán azt értjük, hogy az első n darab elem kikerül a sorozatból és a sorrendet megtartva a sorozat végére kerül. Ezt nevezzük balra forgatásnak, mert a sorozat elemei, az eredetihez képest balra tolódnak. Ennek a párja a jobbra forgatás, amikor a sorozat végéről kikerülő n darab elem a sorrendet megtartva a sorozat elejére kerül. A forgatás (rotation) szó helyett szokták a körkörös léptetés (circular shift) kifejezést is használni e műveletre. Az alábbi ábra ezt próbálja szemléltetni úgy, hogy egy betűket tartalmazó sorozatot „körbehajlítunk” azért, mert így könnyebben felismerhető az elemek balra és jobbra történő mozgása.

Most azt nézzük meg, hogy programkódban hogyan, milyen nyelvi eszközökkel lehet egy sorozat elemeit forgatni. Ha utána nézünk a témának, akkor általában listára találunk példakódokat. Ez azonban leszűkíti a lehetőségeket, sőt esetleg még azt a képzetet is keltheti, hogy csak lista elemeit lehet körkörösen léptetni. Továbbá, sok esetben nincs magyarázat arra, hogy az alternatív megvalósítási módok közül milyen szempontok szerint válasszunk. Ha van is ilyen elemzés, akkor az többnyire a futási idők különbségét taglalja. De, ha az idő nem lényegi szempont (mert pl. rövid sorozatokkal dolgozunk), akkor marad a kérdés.

Ezért e bejegyzésben olyan függvényeket írunk, amelyek abban különböznek, hogy milyen jellemzők meglétét feltételezzük az argumentumként átadott sorozat típusú konténerről. Mindegyik függvény kétparaméteres. Az első fogadja a sorozatot, a második a léptetésszámot. Ha ez pozitív szám, akkor balra léptetünk, ha negatív, akkor jobbra. Közös továbbá a bemutatott függvényekben, hogy mindegyik az argumentumként megadott sorozat típusával megegyező típusú, új konténerobjektumot ad vissza. /Változtatható konténeren önmagán belül történő elemforgatással nem foglalkozunk, mert arra sok példa található máshol/

Megjegyezzük még, hogy a beépített típusok közül a range konténerre az elemforgatás nem értelmezhető, mert annak elemei csak monoton növekvő vagy csökkenő sorrendben követhetik egymást.

A forgatást megvalósító függvények a következők:

Az első függvény a forgatást szeletképzéssel valósítja meg. Lényegében követi az elemforgatás definícióját, mert a bemenő sorozatból kiveszi az első n elemet, meg egy másik szeletben a többit, és megcserélve összefűzi őket. Ez a függvény tehát olyan sorozat típusú konténerekre alkalmazható, amelyekre értelmezett a szeletképzés (slicing) és az összefűzés (concatenation) művelete. Ennek megfelelően a beépített típusokból – a range kivételével – minden sorozat típus, azaz a list, tuple, str, bytes, bytearray lehet argumentum, hiszen ezek mindegyikére alkalmazható a szeletképzés és összefűzés. Ha egyéni sorozattípust megvalósító osztályt hozunk létre, akkor ennek is kell tudnia. Ezt azért kell megemlíteni, mert ahhoz, hogy egy objektum sorozat típusú legyen ezek nem szükséges feltételek, hanem csak az, hogy konténer legyen és indexszel ki lehessen nyerni a megfelelő elemet, a hossza a len() függvénnyel lekérdezhető legyen, valamint az elemek fordított sorrendben kikérhetők legyenek a reversed() függvénnyel.

A második függvény enyhít azon az előfeltételen, hogy a sorozatra értelmezhetőnek kell lenni az összefűzés műveletnek, és ennek megfelelően a megvalósításban ez nem is szerepel. Helyette az első függvényben látott szeletképzés módszerrel előállított részsorozatokat egy tuple konténerbe sorrendhelyesen kicsomagolja. Mivel itt a végeredmény eltérő típusú lehet az argumentum típusától, ezért ez utóbbi konstruktorába helyezzük a kapott tuple-t, és ezzel térünk vissza. Ez azt jelenti, hogy az argumentum típusával egyező, de forgatott elemű sorozatot kapunk. Most az argumentumnak ugyan nem kell tudnia az összefűzést, de annak a feltételnek meg kell felelnie, hogy a példány a típuskonstruktorba helyezett iterálható objektum elemeiből létre tudjon jönni. E feltételnek a fentebb felsorolt beépített sorozat típusokból csak a str nem felel meg. Tehát ez a függvény karakterláncok forgatására nem alkalmas.

A harmadik és negyedik függvénynél csak ez utóbbi új előfeltétel – vagyis, hogy a sorozatpéldány a típuskonstruktorba helyezett iterálható objektum elemeiből létre tudjon jönni – kell, hogy teljesüljön, és nem szükséges, hogy a sorozatkonténerre a szeletképzés értelmezett legyen. A két függvény csak a megvalósítás módjában tér el. A harmadik függvény a modulo osztás jellemzőit használja ki, és a forgatott elemeket egy listaépítő kifejezéssel gyűjti egybe. A negyedik függvény a szabványos könytár collections moduljának deque, azaz kétvégű sor objektumát használja, mert annak van egy éppen a célunknak megfelelő rotate() metódusa. /Mivel a forgatási irány ennél a mi konvenciónkkal ellentétes, ezért van az argumentumban egy negatív előjel/

A deque objektumról részletes leírást és a használatot példákkal magyarázva a Python tudásépítés lépésről lépésre című e-könyv „Sor és verem adatszerkezetek megvalósítása – deque” fejezetben olvashatunk. A szeletképzésről és összefűzésről pedig a „Konténerekkel végezhető műveletek” részben olvashatunk részletesen.

É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.