Bájos python a funkcionális programozásról python, 2. rész

Vigyázz az új cikkekre ebben a sorozatban.

Ez a tartalom a sorozat része: Charming Python

Vigyázz az új cikkekre ebben a sorozatban.

A funkcionális programozás Pythonban a funkcionális programozás (FP) alapfogalmát lefedtük. Ebben a cikkben megpróbálunk egy kicsit mélyebbre menni ebbe a leggazdagabb fogalmi területbe. A Bryn Keller Xoltar Toolkit könyvtára felbecsülhetetlen értékű lesz ebben. Fő jellemzők Az FP Keller egy kis effektív modul formájában mutatkozott be a tiszta Pythonban. A funkcionális modul mellett. a Xoltar Toolkit tartalmazza a lusta modult. Támogató struktúrák, "csak akkor, ha szükséges" számítással. Számos funkcionális programozási nyelv támogatja a halasztott számításokat, így ezek a Xoltar Toolkit komponensek sok mindent megtalálnak Önt olyan funkcionális nyelven, mint a Haskell.

Értékek hozzárendelése

A figyelmes olvasó emlékeztet az előző cikkben említett funkcionális technika korlátaira. Kérdés volt, hogy Pythonban semmi sem tiltja a név egy másik értékének a funkcionális kifejezésre való utalását. Egy FP-ben a neveket úgy értjük, hogy csak a hosszabb kifejezések levél csökkenését jelentik, és azt feltételezzük, hogy ugyanaz a kifejezés mindig ugyanazt az eredményt eredményezi. Ha egy adott értékhez már hozzárendelt egy új értéket, akkor ez a feltételezés sérül. Tegyük fel, hogy a függvényprogramunkban néhány rövidítést definiálunk, mint a következő példában:

Listázás 1. A Python FP session újbóli hozzárendelése bajt okoz

>>> autó = lambda LST: lst [0] >>> CDR = lambda LST: LST [1:] >>> sum2 = lambda LST: autó (LST) + autó (CDR (LST)) >>> sum2 ( tartomány (10)) 1 >>> autó = lambda lst: lst [2] >>> sum2 (tartomány (10)) 5

Sajnálatos módon ugyanazon kifejezés sum2 (tartománya (10)) a program két helyén értékelnek különböző eredményeket, annak ellenére, hogy a kifejezés argumentumok nem változó változók.

Listázás 2. Python FP session védelem az átrendelés ellen

>>> funkcionális import * >>> legyen = kötések () >>> let.car = lambda LST: lst [0] >>> let.car = lambda LST: lst [2] lőfegyverét (legbelső utolsó): Fájl ""1-es vonal, a fájl." D: \ tools \ functional.py "vonal 976, a __setattr__ fel BindingError, a" kötődés '% s' nem lehet módosítani „% name functional.BindingError :. Binding 'autó' nem lehet módosított >>> autó (tartomány (10)) 0

Természetesen egy valódi programnak el kell fogadnia és feldolgoznia kell a BindingError kivételt, de a gerjesztés ténye elkerüli a problémák egész osztályát.

Amellett, hogy az osztály Bindings. A funkcionális névtér funkciót tartalmaz. hozzáférést biztosít a névtérhez (valójában a szótárhoz) a Bindings osztály egy példányából. Ez nagyon hasznos, ha ki kell értékelned egy kifejezést a Bindingsben definiált (nem módosítható) névtérben. A Python eval () függvénye lehetővé teszi, hogy kiszámoljon egy névtérben. A következő példa megmagyarázza, amit mondtak:

Listázás 3. Python FP munkamenet változatlan névterek használatával

>>> hagyja = kötés () # A "valódi" funkció nevek >>> let.r10 = tartomány (10) >>> let.car = lambda lst: lst [0] >>> let.cdr = lambda lst: lst [1:] >>> eval ( 'autó (R10) + autó (cdr (R10))' névtér (let)) >>> inv = kötés () # "Inverz lista" funkció nevét >>> inv. r10 = let.r10 >>> inv.car = lambda LST: lst [-1] >>> inv.cdr = lambda LST: LST [: - 1] >>> Eval ( „autó (R10) + autó (CDR (r10)), névtér (inv)) 17

Az AF-ben nagyon érdekes koncepció - lezárás. Valójában ez az ötlet olyan sok fejlesztő számára csábító volt, hogy még a nem funkcionális programozási nyelvek, például a Perl és a Ruby esetében is megvalósul. Ezenkívül úgy tűnik, hogy a Python 2.1-ben a lexikális kontextust elkerülhetetlenül be kell vonni [1], ami 99% -kal közelebb hozza a lezárásokat.

Mi a zárás? Steve Majewski figyelemreméltóan írta le ezt a koncepciót a Python-val kapcsolatos hálózati konferenciák egyikében: egy objektum egy adatgyűjtemény és az ehhez kapcsolódó eljárások. A bezárás egy eljárás, amelyhez hozzá van kötve egy adatkészlet.

Más szóval, a bezárás olyan, mint a funkcionális Dr. Jekyll, az objektumorientált Mr. Haiduhoz (vagy talán fordítva). A bezárás, valamint az objektum egy példánya a funkcionalitás és az adatok bemutatásának egyik módja, összekapcsolva és összeillesztve.

Menjünk vissza egy kicsit, hogy megértsük, hogy mind a tárgyak és a lezárások milyen problémát oldanak meg, és hogy ez a probléma hogyan megoldódik nélkülük. Általában egy függvény által visszaadott eredményt a számítás során a kontextus határozza meg. A kontextus legáltalánosabb - és talán legnyilvánvalóbb - módja a paraméterek átadása a függvénynek, amely jelzi, hogy mely értékeket kell feldolgozni. De néha nagyon nyilvánvaló különbség van a "háttér" és a "prioritás" paraméterek között - attól függően, hogy egy adott pillanatban melyik függvény működik, és hogyan konfigurálható több potenciális hívás végrehajtására.

Számos módon támogathatjuk a hátteret, elsősorban a hangsúlyt. Például egyszerűen átmásolhatja minden szükséges argumentumot egy függvényre minden egyes híváskor. Gyakran előfordul, hogy egy sor értéket (vagy többtagú struktúrát) átad az egész hívási sorozatban, hogy az adatokat átadja, ahol szükség van rá. Ezt egy egyszerű példa szemlélteti:

Listázás 4. Python session, amely bemutatja a rakományváltozót

Ebben a példában a b () függvényben lévő n paraméter csak akkor szükséges, ha rendelkezésre áll c () Egy másik lehetséges megoldás a globális változók használata.

Listázás 5. Egy Python munkamenet, amely egy globális változó használatát mutatja

>>> N = 10 >>> defaddN (i). globális N visszatérés i + N. >>> addN (7) # Globális N hozzáadása az argumentumhoz 17 >>> N = 20 >>> addN (6) # Globális N hozzáadása az argumentumhoz 26

Az N globális változó bármikor elérhető, függetlenül attól, hogy hol hívod addN () -et. Ebben az esetben nem szükséges kifejezetten átvinni a hátteret. Valamivel több "Python" technika van egy függvényben egy változó "befagyasztása", a függvénydefiníció során az alapértelmezett paraméterérték használatával:

Listázás 6. Python session, amely bemutatja a fagyasztott változót

>>> N = 10 >>> defaddN (i, n = N). visszatérés i + n. >>> addN (5) # Add 10 15 >>> N = 20 >>> addN (6) # Add 10 (az aktuális N nem számít) 16

A befagyasztott változó, valójában a lezárás. Egyes adatok az addN () függvényhez vannak csatolva. Teljes lezárás esetén a funkció leírása idején rendelkezésre álló adatok rendelkezésre állnak, amikor felhívták. Azonban ebben a példában (és még sok más esetben is) egyszerűen hozzáférést biztosít az elégséges adatokhoz az alapértelmezett beállítások használatával. Végül is olyan változók, amelyeket az addN () függvény nem használ. nem játszanak szerepet a számításában.

Nézzük meg az objektum megközelítést egy kicsit sürgetőbb problémával. Ebben az évszakban a gondolatokat általában az adó kiszámításához használt interaktív programok foglalják el. Különböző adatokat gyűjtenek - nem feltétlenül egy bizonyos sorrendben -, majd egy bizonyos időpontban használják őket a számítás során. Hozzunk létre egy ilyen program egyszerűsített verzióját:

Listázás 7. Python-stílusú osztály az adó kiszámításához

osztály TaxCalc: deftaxdue (self): return (self.income-self.deduct) * self.rate taxclass = TaxCalc () taxclass.income = 50000 taxclass.rate = 0.30 = taxclass.deduct 10000 print "Pythonic OOP esedékes adókat =" , taxclass.taxdue ()

TaxCalc-osztályunkban (pontosabban annak példáján) összegyűjthetjük az adatokat - bármilyen sorrendben - és amint minden szükséges elemünk van, felhívhatjuk az objektum metódusát az összegyűjtött adatok feletti számítás elvégzésére. Minden a gyűjteményben összegyűjtött, így különböző példányok különböző adatkészleteket határozhatnak meg. Olyan példányok készítése, amelyek egymástól csak az adataikkal eltérnek, lehetetlen globális vagy "fagyasztott" változók használata esetén. A "brute-force" megközelítés megoldhatja ezt a problémát, de láthatjuk, hogy valódi példában több értéket is át kell adnia. Most nézzük meg, hogyan tudja megoldani ezt a problémát az OOP segítségével az üzenetek átadásával (ez hasonló a Smalltalkhoz vagy az Self-hez, valamint az objektum-orientált xBase változatokhoz):

Listázás 8. Smalltalk-stílusú (Python) adószámítás

osztály TaxCalc: deftaxdue (self): visszatérő (self.income-self.deduct) * self.rate defsetIncome (self, jövedelem): self.income = jövedelem visszatérő magától defsetDeduct (self, levonja): self.deduct = adólevonási visszatér önálló defsetRate (self, sebesség) :. self.rate = ráta visszatér önálló print "Smalltalk-style esedékes adók =" \ TaxCalc () setIncome (50000) .setRate (0,30) .setDeduct (10000) .taxdue ()

Az egyes telepítők által végzett önkéntes visszatérés lehetővé teszi számunkra, hogy az egyes példányok hívásakor az aktuális példányt kezeljük. Amint az a folytatásban látható, ez a megközelítésnek érdekes tulajdonságai vannak a fázisátmenetben történő bezárás használatával.

A Xoltar eszközkészlet használatával olyan teljes lezárásokat hozhat létre, amelyek rendelkeznek az adatok kombinálásához szükséges funkcióval, valamint különböző adatkészleteket tartalmazó több lezárással:

Listázás 9. Python Funkcionális-stílusú adó számítások

a funkcionális behozatalból * taxdue = lambda. (Jövedelem-vonjuk) * ráta incomeClosure = lambda jövedelem, taxdue: lezárás (taxdue) deductClosure = lambda levonására, taxdue: lezárás (taxdue) rateClosure = lambda arány, taxdue: lezárás (taxdue) taxFP = taxdue taxFP = incomeClosure (50000, taxFP ) taxFP = rateClosure (0,30, taxFP) taxFP = deductClosure (10000, taxFP) print "Funkcionális esedékes adók =" taxFP () print "Lisp-style esedékes adók =" \ incomeClosure (50000, rateClosure (0,30 deductClosure (10000 , taxdue))) ()

Minden általunk leírt zárófunkció a hatályában meghatározott értékeket veszi fel, és ezeket az értékeket a funkcionális objektum globális hatókörébe köti. Azonban ennek a funkciónak a globális hatóköre nem feltétlenül egyezik meg a modul "valós" globális hatókörével és egy másik lezárás globális kiterjedésével. A bezárás egyszerűen "adatokat hordoz magával".

Példánkban, hogy bizonyos értékeket a bezárás körébe helyezünk, számos különféle funkciót használunk (jövedelem, levonás, árfolyam). Elég lenne egyszerűen megváltoztatni a tervet, így önkényes értékeket rendelhet. Ezenkívül a szórakozás kedvéért ebben a példában két, kissé eltérő funkcionális stílust használunk. Az első egymás után további értékeket köti össze a hiba területével; A taxFP módosítása lehetővé teszi, hogy a sorok hozzáadását a bezáráshoz bármely sorrendben megjelenjenek. Ha azonban olyan változó neveket használunk, mint a tax_with_Income. Meg kell szerveznünk a kötéseket egy bizonyos sorrendben, és át kell adnunk a korábbiakat a másikra. Mindenesetre, amint minden szükséges, egy bezáráshoz kötődik, hívhatjuk a megnövekedett funkciót.

A második stílus véleményem szerint valamivel hasonlít a Lisp-hez (kizárólag a zárójelek miatt). Az esztétika mellett a második stílusban két érdekes pillanat van. Először is, nincs névmegkötés. A második stílus egyetlen kifejezés, anélkül, hogy irányelveket kellene alkalmazni. (lásd az előző cikket, ami megmagyarázza, hogy ez miért játszik szerepet).

A Lisp-stílus másik érdekes részlete az, hogy mennyire használják a bezárásokat a fentiekben említett La Smalltalk üzenetek küldésére. Mindkét esetben az értékek felhalmozódnak a function / method taxdue () meghívása előtt (mindkét egyszerűsített változat kivételeket ad, ha a szükséges adatok nem állnak rendelkezésre). A smalltalk stílus minden lépésben átmegy egy tárgyat, míg a Lisp stílus folytatás. De ha megnézzük a gyökeret, akkor a funkcionális és tárgyorientált programozás majdnem ugyanazt jelenti.

Farok rekurzió

Források letöltése

Kapcsolódó témák