Hogyan használjuk a reduce() függvényt, ha a redukció során az indexet is figyelembe kell venni, illetve a közbenső részeredményt konstanssal kell szorozni?

A functools modul reduce() függvényét általában akkor javasolt használni, ha a feladatot valamilyen nyelvi szerkezettel (pl. konténerépítő kifejezéssel), vagy beépített redukáló függvénnyel (all(), any(), len(), max(), min(), sum()) nem, vagy csak körülményesen lehet megoldani.

Az alábbi ábra A) részében egy egyszerű halmozott összeadás képletét és rendszertechnikai blokksémáját látjuk. Ha az a feladat, hogy az 1,2,3 sorozat összegét képezzük, akkor ezt a reduce() függvénnyel könnyen meg tudjuk oldani, hiszen az összeadást végző függvényt és a sorozatot argumentumként kell csak egyszerűen megadni. A függvényre egyetlen követelmény van, hogy két pozícionális paraméterrel kell, hogy rendelkezzen.

De mi van akkor, ha egy problémát megoldó modellünk olyan képletet eredményez, amely függ a sorozatindextől, ráadásul a közbenső részeredményeket is meg kell szorozni egy konstans értékkel. Egy ilyen esetet mutat az ábra B) része.

A gond ugyebár az, hogy a reduce()-on belül, annak argumentumban megadott függvény kumulatív hívásaihoz index átadására nincs lehetőség, és a részeredményekhez sem férünk hozzá kívülről, hogy egy konstanssal szorozzunk.

Ennek ellenére ilyenkor is használható a reduce(), ha az első paraméterének átadandó függvényt megfelelően definiáljuk. Azt mondtuk, hogy a követelmény a két pozicionális paraméter megléte. De az nem tilos, hogy további alapértelmezett értékkel rendelkező paramétereket adjunk meg. Egyik ilyen a konstans értéket hordozó lesz. A másikból pedig az aktuális indexet fogjuk megkapni. A kérdés, hogy hogyan.

Az indexképzésnél az a megoldandó probléma, hogy a reduce() belsejében a függvényünk újra és újra meg lesz hívva, tehát ahhoz, hogy egy új index előállításához ne legyen elfelejtve az előző indexérték, olyan objektumot kell használni, amely erre emlékszik. Az ábrán két ilyen megoldást mutatunk. Az első az itertools modul count() iterátorát alkalmazza, a másodikban egy listát használunk. Ez még önmagában nem lenne elég, viszont kihasználjuk a Python azon jellegzetességét is, hogy egy paraméter alapértelmezett értéke nem híváskor, hanem a függvény definíciójának értelmezésekor jön létre. Ez fogja biztosítani, hogy a definíciókor létrejövő count(), illetve lista objektumok a reduce() lefutása végéig ugyanazok maradnak a belső ismételt hívások ellenére.

E témakörrel a Python tudásépítés lépésről lépésre című e-könyv „Változtatható objektum használata alapértelmezett paraméterértékként” fejezete foglalkozik.

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