Funkcionális programozás Pythonban. Mikor és hogyan használjuk?
A funkcionális programozás egyike a programozási paradigmáknak, módszertanoknak. A funkcionális programozás során a programozási feladatot függvények kiértékeléseként határozhatjuk meg. Tehát inkább az a fontos, hogy MIT kell kiszámítanunk és nem az, hogy HOGYAN, milyen lépésekben.
Például a rekurzió is a funkcionális programozás egyik igen fontos eleme, amellyel kiválthatók az iterációs utastások, ciklusok.
Egy átlagos Python kódban kis része van a funkcionális programozásnak, de azért jó tisztában lenni ezekkel az elemekkel, hiszen mások kódjait olvasva sokszor találkozhatunk velük.
Funkcionális programozás
A funkcionális programozásban a program csak olyan függvényeket tartalmaz, amelyek nem módosítják a hívó környezetüket. Tehát a függvények kimenete csak a bemenetüktől függ, és látható módon nem befolyásolják azt a helyet, ahonnan meghívták őket.
Vannak olyan programozási nyelvek, amelyek tisztán a funkcionális programozással dolgoznak, például a Haskell, az Erlang vagy a Lisp. A Python támogatja a funkcionális programozást, de tartalmaz elemeket egyéb programozási paradigmákból is.
Funkcionális programozás a Pythonban
A Pythonban lehetséges, hogy egy függvénynek függvény bemeneti paramétere legyen, sőt függvény kimenetet is képes produkálni.
def fuggveny(a, b):
return a + b
def masik_fuggveny(valamilyen_fuggveny_parameter, x, y):
return valamilyen_fuggveny_parameter(x, y)
print(masik_fuggveny(fuggveny, 1, 2))
>>> 3
Most csináljunk egy függvényt, amely létrehoz egy másik függvényt.
def harmadik_fuggveny():
def f():
print("Engem egy függvény hozott létre.")
return f
negyedik_függvény = harmadik_fuggveny()
negyedik_függvény()
>>> Engem egy függvény hozott létre.
# vagy akár
harmadik_fuggveny()() # ezzel ugyanazt érjük el
>>> Engem egy függvény hozott létre.
Mivel Pythonban gyakorlatilag minden objektum, ezért ez alól a függvények sem lesznek kivételek. Amit meg lehet csinálni egy szöveges vagy egy szám változóval, azt meg lehet csinálni egy függvénnyel is.
def fuggveny(a, b):
return a + b
# készítünk egy másik hivatkozást az eredeti függvényünkhöz
masik_fuggveny = fuggveny
print(fuggveny) # kinyomtatjuk a függvény azonostóját
print(masik_fuggveny) # most a másik függvény azonosítóját, ami azonos lesz
print(masik_fuggveny(1, 2)) # végül csinálunk egy függvényhívást
>>> <function fuggveny at 0x000002554814E0D0>
>>> <function fuggveny at 0x000002554814E0D0>
>>> 3
lista = ["pk", fuggveny, 12] # készítünk egy listát, amely 2. tagja egy függvény
print(lista[1]) # függvényhívás
print(lista[1](1, 2))
>>> <function fuggveny at 0x000002554814E0D0>
>>> 3
konyvtar = {1: "pk", 2: fuggveny, 3: 12} # egy könyvtár készítése egy függvénnyel
print(konyvtar[2])
print(konyvtar[2](1, 2)) # függvényhívás
>>> <function fuggveny at 0x000002554814E0D0>
>>> 3
Átadhatunk tehát egy függvénynek egy másik függvényt bemenetként, amely módosítja a hatását. Erre talán az egyik legjobb példa a sort
függvény, amellyel sorrendbe tudjuk rakni egy lista elemeit.
lista = ["Jabba", "Han", "Luke"]
print(lista)
print(sorted(lista)) # alapértelmezetten ABC sorrendbe rakjuk az elemeket
print(sorted(lista, key=len)) # a sorrendbe rakás kulcsa a len függvény lesz
# Ez azt jelenti, hogy a lista minden elemén meghívjuk a len függvényt, és az eredmény alapján rakjuk sorrendbe az elemeket.
print(sorted(lista, key=len, reverse=True)) # csináljuk meg fordított sorrendben ugyanezt.
# Csinálunk egy saját függvényt, a len helyett, amely kimenete a szavak negatív hossza lesz.
def forditott_sorrend(elem):
return -len(elem)
print(sorted(lista, key=forditott_sorrend))
>>> ['Jabba', 'Han', 'Luke']
>>> ['Han', 'Jabba', 'Luke']
>>> ['Han', 'Luke', 'Jabba']
>>> ['Jabba', 'Luke', 'Han']
>>> ['Jabba', 'Luke', 'Han']
Névtelen függvények, lambda függvények
Vannak olyan esetek, amikor szükségtelen megadnunk egy nevet a létrehozott függvényünknek, mert például csak egy rendezéshez használjuk, mint a fentiekben. Emellett, ha egyszerűbb függvényekről van szó, akkor akár lambda
függvényekkel is létre tudjuk hozni őket.
# egy olyan függvény, amely a megkapott x paramétert visszafelé írja ki szeletelés segítségével
visszafele = lambda x: x[::-1]
print(visszafele("Az erő veled van."))
>>> .nav delev őre zA
# vagy akár
print((lambda x: x[::-1])("Az erő veled van."))
>>> .nav delev őre zA
A lambda
függvények egyszerűsített függvény definíciók. Szerkezetük a következő:
lambda <paraméterlista>: <képlet, kifejezés>
print((lambda x, y, z: (x + y + z) * 10)(1, 2, 3))
>>> 60
Természetesen ezt megoldhattuk volna a hagyományos módszerrel is.
def visszafele_2(y):
return y[::-1]
print(visszafele_2 ("Az erő veled van."))
>>> .nav delev őre zA
Most nézzük meg az előző sorendberakós példát lambda
függvény segítségével.
lista = ["Jabba", "Han", "Luke"]
print(sorted(lista, key=lambda elem: -len(elem)))
>>> ['Jabba', 'Luke', 'Han']
Azt is megtehetjük, hogy nem adunk paramétert a lambda
függvényünknek, hiszen nem kötelező.
szam_generator = lambda: 123456
>>> szam_generator()
>>> 123456
Ha egy gyűjteményes adattípusban szeretnénk visszaadni az eredményeket, akkor azt is megtehetjük.
print( (lambda x: (x, x*2, x*3))(3)) # ki kell tennünk a zárójeleket
>>> (3, 6, 9)
Alkalmazzunk egy függvényt a lista minden elemére
Egyszer volt, hol nem volt az iterátor objektum. Ez tulajdonképpen egy olyan Python objektum, amellyel végig lépegethetünk egy listán, vagy más gyűjteményes adattípuson. Nézzünk rá egy példát.
a = ["Jabba", "Han", "Luke", "Yoda", "R2D2", "3CPO"]
iterator_objektum = iter(a) # itt létrehozunk egy iterátor objektumot az iter függvénnyel
print(iterator_objektum)
>>> <list_iterator object at 0x00000217BE492EB0>
print(next(iterator_objektum))
print(next(iterator_objektum))
print(next(iterator_objektum))
print(next(iterator_objektum))
print(next(iterator_objektum))
print(next(iterator_objektum))
>>> Jabba
>>> Han
>>> Luke
>>> Yoda
>>> R2D2
>>> 3CPO
Mivel az iterátor objektumot a fenti sorokban kiürítettük, ezért, ha most visszaalakítjuk egy egyszerű listává, akkor üres listát fogunk kapni. Így működik az iterátor objektum.
print(list(iterator_objektum))
>>> []
Most használjuk a map
függvényt, amellyel meg tudjuk oldani a fenti feladatot. Lépegessünk végig minden elemen, és futtassuk minden elemre a megadott függvényt. A map
függvény első paramétere a függvény, amelyet futtatni szeretnénk minden elemre, a második paramétere pedig a gyűjteményes adattípus lesz.
nevek = ["Jabba", "Han", "Luke", "Yoda", "R2D2", "3CPO"]
iterator_objektum = map(len, nevek)
print(iterator_objektum)
print(list(iterator_objektum)) # itt átalakítjuk listává az iterátor objektumot
>>> <map object at 0x000001C5F292DF40>
>>> [5, 3, 4, 4, 4, 4]
Egy egyszerű for ciklussal is végig lépkedhetünk az iterátor objektumon:
for i in iterator_objektum:
print(i, end=” ”)
>>> 5 3 4 4 4 4
Most használjunk lambda függvényt a map
függvény paramétereként.
nevek = ["Jabba", "Han", "Luke", "Yoda", "R2D2", "3CPO"]
iterator_objektum = map(lambda x: x[::-1], nevek) # fordítsunk meg minden szót
print(list(iterator_objektum)) # itt átalakítjuk listává az iterátor objektumot
>>> abbaJ naH ekuL adoY 2D2R OPC3
A map
függvényben több listát is elhelyezhetünk. Ilyenkor a listák azonos sorszámú elemeiből fog dolgozni.
def f(x, y, z):
return x + y + z
print(list(map(f, [1, 2, 3], [10, 20, 30], [100, 200, 300])))
>>> [111, 222, 333]
Nézzük ezt meg lambda
függvénnyel.
print(list(map((lambda a, b, c: a + b + c), [1, 2, 3], [10, 20, 30], [100, 200, 300])))
>>> [111, 222, 333]
Hogyan válasszunk ki bizonyos feltételnek megfelelő elemeket egy listából?
A map
függvényhez rendkívül hasonló filter
függvény lesz a segítségünkre.
print(list(filter(lambda x: x > 100, [1, 100, 2, 200, 3, 300])))
>>> [200, 300]
Ebben az esetben a filter
függvény megfogja a lambda
függvénnyel megadott paraméter függvényt, és annak alapján adja vissza a lista elemeit. Minden elemre futtatja a lambda
függvényt, amelynek visszatérési értéke igaz vagy hamis értéket vehet fel. Ha ez az érték igaz akkor a filter
függvény ki fogja írni a soron lévő elemet, ha hamis az érték, akkor nem. Ilyen egyszerű a dolog.
Nézzünk még egy példát.
print(list(filter(lambda x: x % 2 and x % 15 == 0, range(100))))
# Tehát ha a range függvénnyel előállított számsor adott eleme NEM osztható 2-vel és OSZTHATÓ 15-tel is, akkor ki fogja írni az adott számot. Mivel az x % 2 akkor lesz False, ha értéke 0, vagyis, ha osztható 2-vel. Trükkös.
>>> [15, 45, 75]
És még egy példa.
nevek = ["Jabba", "Han", "Luke", "Yoda", "R2D2", "3CPO"]
print(list(filter(lambda x: x.isupper(), nevek)))
# Tehát ha az adott lista elem nagy betűkkel van írva, akkor ki fogja írni az adott nevet.
>>> ['R2D2', '3CPO']
Listaértelmezés
Az igazsághoz hozzá tartozik, hogy a map
és filter
függvények szerepét a Pythonban remekül eljátssza a listaértelmezés is. Nézzünk még rá egy-egy példát befejezésül.
# a map függvény alternatívája listaértelmezéssel
nevek = ["Jabba", "Han", "Luke", "Yoda", "R2D2", "3CPO"]
print([x[::-1] for x in nevek])
>>> ['abbaJ', 'naH', 'ekuL', 'adoY', '2D2R', 'OPC3']
# és a filter függvény alternatívája listaértelmezéssel
print([x for x in range(100) if x % 2 and x % 15 == 0])
>>> [15, 45, 75]
Ha mélyeben érdekelnek a Python nyelv rejtelmei, ajánlom figyelmedbe a most már külön is megvásárolható Python START kurzusunkat: https://programozas-karrier.teachable.com/p/python/
Utószó
Szeretnéd a programozás segítségével megváltoztatni az életed? Itt az idő! Legújabb könyvünkből megtudhatod, hogyan fogj hozzá egy ilyen összetett feladathoz: https://leanpub.com/mi-kell-a-programozoi-karriervaltashoz/