Informatika2-2012/Kukaba12
(→Generátorkifejezések) |
|||
25. sor: | 25. sor: | ||
=== Függvényközpontú programozás === | === Függvényközpontú programozás === | ||
− | ==== A függvényközpontú | + | ==== A függvényközpontú programozás klasszikus elemei a Pythonban ==== |
A <tt>lambda</tt> kifejezések névtelen függvények, alakjuk: | A <tt>lambda</tt> kifejezések névtelen függvények, alakjuk: | ||
31. sor: | 31. sor: | ||
lambda paraméterek: kifejezés | lambda paraméterek: kifejezés | ||
</python> | </python> | ||
− | ahol a <tt>paraméterek</tt> egy esetleg üres lista, a <tt>kifejezés</tt> pedig egy elágazást, ciklust, <tt>return</tt> vagy <tt>yield</tt> utasítást nem tartalmazó kifejezés lehet. | + | ahol a <tt>paraméterek</tt> egy esetleg üres lista, a <tt>kifejezés</tt> pedig egy elágazást, ciklust, <tt>return</tt> vagy <tt>yield</tt> utasítást nem tartalmazó (de esetleg feltételes) kifejezés lehet. Csak nagyon egyszerű (félsoros) függvényekre használjuk! |
<python> | <python> | ||
lambda x: x ** 2 | lambda x: x ** 2 | ||
38. sor: | 38. sor: | ||
</python> | </python> | ||
− | A ciklusok elkerülésére három egyszerű | + | A ciklusok elkerülésére három egyszerű ,,klasszikus'' függvény szolgál: |
* megfeleltetés -- egy függvény egy bejárható (iterable) objektum minden elemére hat: <tt>map()</tt> | * megfeleltetés -- egy függvény egy bejárható (iterable) objektum minden elemére hat: <tt>map()</tt> | ||
<python> | <python> | ||
− | + | map(lambda x: x ** 2, [1, 2, 3, 4]) # [1, 4, 9, 16] | |
+ | map(lambda x, y: x + y, [1, 2, 3, 4], [0, 10, 100, 1000]) | ||
+ | # [1, 12, 103, 1004] | ||
+ | map(lambda x, y: x ** y, itertools.repeat(2)[1, 2, 3, 4], [0, 10, 100, 1000]) | ||
+ | # [1, 12, 103, 1004] | ||
</python> | </python> | ||
* szűrés -- egy bejárható objektumnak csak azokat az elemeit tartjuk meg, amelyre egy függvény <tt>True</tt> értéket ad: <tt>filter()</tt> | * szűrés -- egy bejárható objektumnak csak azokat az elemeit tartjuk meg, amelyre egy függvény <tt>True</tt> értéket ad: <tt>filter()</tt> | ||
<python> | <python> | ||
− | + | filter(lambda x: x > 0, [-1, 2, -3, 4]) # [2, 4] | |
</python> | </python> | ||
− | * | + | * redukálás: <tt>reduce()</tt> (3.0-tól a standard könyvtárból törölve: <tt>functools.reduce()</tt>) |
<python> | <python> | ||
reduce(lambda x, y: x + y, [1, 2, 3, 4])) # 10 | reduce(lambda x, y: x + y, [1, 2, 3, 4])) # 10 | ||
62. sor: | 66. sor: | ||
for elem in bejárható_objektumN if feltételN] | for elem in bejárható_objektumN if feltételN] | ||
</python> | </python> | ||
− | Megadjuk a fenti első két | + | Megadjuk a megfeleltetés és szűrés fenti első két példája e szintaktika szerinti változatát: |
<python> | <python> | ||
[x ** 2 for x in [1, 2, 3, 4]] # [1, 4, 9, 16] | [x ** 2 for x in [1, 2, 3, 4]] # [1, 4, 9, 16] | ||
72. sor: | 76. sor: | ||
</python> | </python> | ||
A [http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#Python_Language_Rules Google] is azt javasolja, hogy map, filter, reduce helyett használjunk listaértelmezéseket vagy for ciklust, jobban olvasható. | A [http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#Python_Language_Rules Google] is azt javasolja, hogy map, filter, reduce helyett használjunk listaértelmezéseket vagy for ciklust, jobban olvasható. | ||
+ | |||
+ | Az egymásba ágyazott ciklusok is helyettesíthetőek listaértelmezéssel. | ||
+ | <python> | ||
+ | for x in (1,2,3): | ||
+ | for y in [1,2,3,4]: | ||
+ | if x < y: | ||
+ | print (x, y, x*y), | ||
+ | # (1, 2, 2) (1, 3, 3) (1, 4, 4) (2, 3, 6) (2, 4, 8) (3, 4, 12) | ||
+ | </python> | ||
+ | E helyett írható a következő kód: | ||
+ | <python> | ||
+ | [(x, y, x * y) for x in (1,2,3) for y in [1,2,3,4] if x < y] | ||
+ | # [(1, 2, 2), (1, 3, 3), (1, 4, 4), (2, 3, 6), (2, 4, 8), (3, 4, 12)] | ||
+ | </python> | ||
+ | |||
==== Egy példa: a nyolc királynő ==== | ==== Egy példa: a nyolc királynő ==== |
A lap 2012. május 1., 10:56-kori változata
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ás klasszikus elemei a 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ó (de esetleg feltételes) kifejezés lehet. Csak nagyon egyszerű (félsoros) függvényekre használjuk!
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ű ,,klasszikus függvény szolgál:
- megfeleltetés -- egy függvény egy bejárható (iterable) objektum minden elemére hat: map()
map(lambda x: x ** 2, [1, 2, 3, 4]) # [1, 4, 9, 16] map(lambda x, y: x + y, [1, 2, 3, 4], [0, 10, 100, 1000]) # [1, 12, 103, 1004] map(lambda x, y: x ** y, itertools.repeat(2)[1, 2, 3, 4], [0, 10, 100, 1000]) # [1, 12, 103, 1004]
- szűrés -- egy bejárható objektumnak csak azokat az elemeit tartjuk meg, amelyre egy függvény True értéket ad: filter()
filter(lambda x: x > 0, [-1, 2, -3, 4]) # [2, 4]
- redukálá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] [kifejezés for elem in bejárható_objektum1 if feltétel1 for elem in bejárható_objektum2 if feltétel2 for elem in bejárható_objektumN if feltételN]
Megadjuk a megfeleltetés és szűrés fenti első két példája 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ó.
Az egymásba ágyazott ciklusok is helyettesíthetőek listaértelmezéssel.
for x in (1,2,3): for y in [1,2,3,4]: if x < y: print (x, y, x*y), # (1, 2, 2) (1, 3, 3) (1, 4, 4) (2, 3, 6) (2, 4, 8) (3, 4, 12)
E helyett írható a következő kód:
[(x, y, x * y) for x in (1,2,3) for y in [1,2,3,4] if x < y] # [(1, 2, 2), (1, 3, 3), (1, 4, 4), (2, 3, 6), (2, 4, 8), (3, 4, 12)]
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.
Az i-edik és j-edik sorban lévő királynő pontosan akkor üti egymást, ha abs(i - j) != abs(x[i] - x[j]). Így a megoldás:
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
Generátorkifejezések
A listaértelmezésekhez hasonló szintaktikával (szögletes helyett kerek zárójelekkel) generátorkifejezés is konstruálható. Ez tehát a next() függvény meghívásakor számítja ki a következő elemet. Szintaktikája:
(kifejezés for elem in bejárható_objektum) (kifejezés for elem in bejárható_objektum if feltétel) (kifejezés for elem in bejárható_objektum1 if feltétel1 for elem in bejárható_objektum2 if feltétel2 for elem in bejárható_objektumN if feltételN)
A alábbi generátorkifejezés mindig a következő négyzetszámot adja:
>>> y = (x**2 for x in range(10)) >>> print y <generator object <genexpr> at 0xb740bb6c> >>> y.next() 0 >>> y.next() 1 >>> y.next() 4 >>> y.next() 9 ....... >>> y.next() 64 >>> y.next() 81 >>> y.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
A következő két program ekvivalens, az egyikben generátorkifejezéssel, a másodikban yield segítségével adunk meg olyan függvényt, mely az első n négyzetszámot generáló függvényt ad vissza.
>>> def kovetkezo_negyzet(n): ... for x in range(n): ... yield x ** 2 ... >>> y = kovetkezo_negyzet(3) >>> print y <generator object kovetkezo_negyzet at 0xb740ba7c> >>> y.next() 0 >>> y.next() 1 >>> y.next() 4 >>> y.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
A második változat generátorkifejezéssel:
>>> def kovetkezo_negyzet(n): ... return (x ** 2 for x in range(n)) ... >>> y = kovetkezo_negyzet(3) >>> print y <generator object <genexpr> at 0xb740baa4> >>> y.next() 0 >>> y.next() 1 >>> y.next() 4 >>> y.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration