Informatika2-2012/Kukaba12
Tartalomjegyzék |
Funkcionális programozás python-ban
Programozási paradigmák
Egy probléma megoldásához többféleképp közelíthetünk. Az, hogy a problémát hogyan bontjuk részekre, meghatározza a használható/andó programnyelvi elemeket. Ennek megfelelően a programnyelvi elemeknek négy fő típusát szokás megkülönböztetni:
- Parancsközpontú (imperative), vagy vele majdnem azonos értelemben használt eljárásközpontú (procedurális, procedural)
- algoritmikus gondolkodás, receptszerű programozás, ,,tedd ezt, majd ezt,..."
- A Neumann-elvű számítógép ötletére épül
- Számítási lépések időben egymás utáni elvégzése (parancsok végrehajtása)
- Tipikus parancsok: értékadás, eljárás meghívása,
- Az eljárások megváltoztatják a program kontrollstruktúráinak állapotát
- Objektumközpontú (objektumorientált, object-oriented)
- Az emberek közti interakciókat és a való világ jelenségeit modellezi
- A fogalmakat osztályok, a jelenségeket objektumok valósítják meg
- Függvényközpontú (funkcionális, functional)
- A függvények matematikai elméletére épül,
- A függvények értéket adnak vissza, de nincs mellékhatásuk, nincsenek változók
- A program függvényhívásokból áll
- A parancsközpontúnál egyszerűbb struktúra, könnyebben bizonyítható a program helyessége, könnyebben tesztelhető, debug-olható
- Logikai, vagy a nála általánosabb értelemben használt deklaratív (logic, declarative)
- A program végrehajtása az axiómáknak és a következtetési szabályoknak (deklarációknak) megfelelő lehetőség(ek) megkeresésből áll
- Matematikai logikai alapú
- Szűkebb alkalmazási lehetőségek
Függvényközpontú programozás
A függvényközpontú programoz klasszikus elemei a korai Pythonban
A lambda kifejezések névtelen függvények, alakjuk:
lambda paraméterek: kifejezés
ahol a paraméterek egy esetleg üres lista, a kifejezés pedig egy elágazást, ciklust, return vagy yield utasítást nem tartalmazó kifejezés lehet.
lambda x: x ** 2 lambda x: "pozitív" if x > 0 else "nem pozitív" lambda x, y: x + y
A ciklusok elkerülésére három egyszerű mód szolgál:
- megfeleltetés -- egy függvény egy bejárható (iterable) objektum minden elemére hat: map()
list(map(lambda x: x ** 2, [1, 2, 3, 4])) # [1, 4, 9, 16]
- szűrés -- egy bejárható objektumnak csak azokat az elemeit tartjuk meg, amelyre egy függvény True értéket ad: filter()
list(filter(lambda x: x > 0, [-1, 2, -3, 4])) # [2, 4]
- elhagyás: reduce() (3.0-tól a standard könyvtárból törölve: functools.reduce())
reduce(lambda x, y: x + y, [1, 2, 3, 4])) # 10
Listaértelmezések (list comprehensions)
A fenti három függvény helyettesítésére szolgál a lista elemeinek a matematikában szokásos halmazmegadási módjára emlékeztető leírása, amelyben ciklus-szerű és feltétel-szerű nyelvi elem is szerepelhet. Alakjuk azonnal érthető:
[kifejezés for elem in bejárható_objektum] [kifejezés for elem in bejárható_objektum if feltétel]
Megadjuk a fenti első két példa e szintaktika szerinti változatát:
[x ** 2 for x in [1, 2, 3, 4]] # [1, 4, 9, 16] [x for x in [-1, 2, -3, 4] if x > 0]
A harmadik esetre Guido van Rossum azt mondja, hogy tipikusan csak asszociatív függvényekre használjuk, mint + vagy *.
sum([1, 2, 3, 4]) # 10
A Google is azt javasolja, hogy map, filter, reduce helyett használjunk listaértelmezéseket vagy for ciklust, jobban olvasható.
Egy példa: a nyolc királynő
Feladat: Hányféleképp lehet nyolc királynőt föltenni egy sakktáblára, hogy semelyik kettő ne üsse egymást?
Azt tudjuk, hogy az ilyen bástyaelhelyezések száma 8! = 40320. A királynők azonban átlósan is üthetik egymást.
from itertools import permutations for x in permutations(range(8)): if all([i==j or abs(i-j) != abs(x[i]-x[j]) for i in range(8) for j in range(8)]): print x