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/

Leave a Comment

Az e-mail-címet nem tesszük közzé.