Un python fermecător despre programarea funcțională în Python, Partea 2

Aveți grijă de articole noi din această serie.

Acest conținut face parte din seria: Charming Python

Aveți grijă de articole noi din această serie.

În articolul precedent "Programarea funcțională în Python" au fost acoperite conceptele de bază ale programării funcționale (FP). În acest articol, vom încerca să mergem mai adânc în această zonă bogată conceptuală. Biblioteca Xoltar Toolkit a lui Bryn Keller va fi de neprețuit în acest sens. Caracteristici cheie FP Keller prezentat sub forma unui mic modul eficient din Python pur. În plus față de modulul funcțional. Xoltar Toolkit include modulul leneș. Structuri de susținere, calculate "numai atunci când este necesar". O mulțime de limbi de programare funcționale suportă calculul amânat, astfel încât aceste componente Xoltar Toolkit vă vor oferi multe din ceea ce puteți găsi într-un limbaj funcțional, cum ar fi Haskell.







Alocarea valorilor

Cititorul atent își amintește limitarea tehnicii funcționale pe care am menționat-o în articolul precedent. A fost o chestiune de faptul că nimic în Python interzice re-alocarea unei alte valori a unui nume care se referă la o expresie funcțională. Într-o FP, numele se înțeleg doar prin reducerea literelor expresiilor mai lungi, și se presupune că aceeași expresie duce întotdeauna la același rezultat. Dacă o valoare nouă este deja atribuită unui anumit nume, această ipoteză este încălcată. Să presupunem că vom defini anumite abrevieri pentru utilizarea în programul nostru de funcții, ca în exemplul următor:

Listing 1. Sesiunea Python FP cu realocare duce la probleme

>>> car = lambda lst: lst [0] >>> cdr = lambda lst: lst [1:] >>> sum2 = lambda lst: car (LST) + car (cdr (LST)) >>> sum2 ( intervalul (10)) 1 >>> masina = lambda lst: lst [2] >>> suma2 (intervalul (10)) 5

Din păcate, aceeași sumă de expresie2 (intervalul (10)) este evaluată la rezultate diferite în două locuri din program, în ciuda faptului că argumentele expresiei nu sunt variabile variabile.

Listing 2. Sesiunea Python FP cu protecție împotriva re-alocării

>>> din import functional * >>> lasa = Bindings () >>> let.car = lambda lst: lst [0] >>> let.car = lambda lst: lst [2] Traceback (cel mai interior ultima): File ""Linia 1, în fișier." D: \ tools \ functional.py "linia 976, în __setattr__ ridica BindingError," Legarea '% s' nu poate fi modificat „name% functional.BindingError :. Legare 'masina' nu poate fi modificat >>> masina (interval (10)) 0

Desigur, un program real trebuie să intercepteze și să proceseze excepția BindingError, însă însăși excitarea lui evită o întreagă clasă de probleme.

În plus față de clasarea Legături. funcțional conține o funcție de spațiu de nume. oferind accesul la spațiul de nume (de fapt, în dicționar) dintr-o instanță a clasei Bindings. Acest lucru este foarte util dacă trebuie să evaluați o expresie într-un spațiu de nume (neschimbat) definit în Legături. Funcția eval () din Python vă permite să calculați într-un spațiu de nume. Următorul exemplu explică ceea ce sa spus:

Listing 3. Sesiunea Python FP utilizând spații de nume neschimbate

>>> lasa = Bindings () # "lumea reală" nume de funcții >>> let.r10 = interval (10) >>> let.car = lambda lst: lst [0] >>> let.cdr = lambda lst: LST [1:] >>> eval ( 'masina (r10) + masina (cdr (r10))', spațiul de nume (sa)) >>> INV = Asocieri () # "Inverted" listă numele de funcții >>> INV. r10 = let.r10 >>> inv.car = lambda lst: lst [-1] >>> inv.cdr = lambda lst: lst [: - 1] >>> eval ( „car (r10) + car (cdr (r10)) ', namespace (inv)) 17

În AF există un concept foarte interesant - închidere. De fapt, această idee a fost atât de tentantă pentru mulți dezvoltatori încât este pusă în aplicare chiar și în limbi de programare non-funcționale, cum ar fi Perl și Ruby. În plus, se pare că în Python 2.1, contextul lexical va fi în mod inevitabil inclus [1], ceea ce ne va apropia de închideri cu 99%.

Deci, ce este închiderea? Steve Majewski a descris remarcabil acest concept într-una dintre conferințele de rețea legate de Python: Un obiect este o colecție de date, împreună cu procedurile asociate cu acesta. Închiderea este o procedură împreună cu un set de date legat de aceasta.

Cu alte cuvinte, închiderea este ceva de genul Dr. Jekyll funcțional în legătură cu domnul Haidu (sau, poate, invers). Închiderea, precum și o instanță a unui obiect, reprezintă o modalitate de prezentare a funcționalității și a datelor, legate și împachetate împreună.

Să ne întoarcem puțin pentru a înțelege ce problemă rezolvă atât obiectele cât și închiderile și cum se rezolvă această problemă fără ele. În mod normal, rezultatul returnat de o funcție este determinat de context în timpul calculului său. Cea mai obișnuită și poate cea mai evidentă modalitate de a defini acest context este de a transmite parametrii funcției care indică ce valori trebuie procesate. Dar, uneori, există o diferență foarte evidentă între parametrii "de fond" și "prioritate" - între ceea ce funcția are la un moment dat și modul în care este configurată să efectueze mai multe apeluri potențiale.







Există o serie de modalități de a susține argumentele de fundal, concentrându-se pe prioritate. De exemplu, puteți să transmiteți fiecărui argument necesar unei funcții de fiecare dată când este apelat. Se întâmplă deseori să treacă o serie de valori (sau o structură cu mai multe membri) pe întreaga secvență de apeluri pentru a transfera datele acolo unde ar fi nevoie. Acest lucru este ilustrat printr-un exemplu simplu:

Listing 4. Sesiunea Python care arată variabilitatea încărcăturii

În acest exemplu, parametrul n din cadrul funcției b () trebuie să fie disponibil numai pentru transferul la c () O altă soluție posibilă este utilizarea variabilelor globale.

Listing 5. O sesiune Python care arată utilizarea unei variabile globale

>>> N = 10 >>> defaddN (i). global N. returnează i + N. >>> addN (7) # Adăugați N global la argumentul 17 >>> N = 20 >>> addN (6) # Adăugați N global la argumentul 26

Variabila globală N este disponibilă în orice moment, indiferent unde numiți addN (). În acest caz, nu este necesar să transferați în mod explicit contextul de fundal. O tehnică mai puțin "Python" este de a "îngheța" o variabilă într-o funcție, folosind valoarea implicită a parametrului în timpul definiției funcției:

Listing 6. Sesiunea Python care ilustrează variabila înghețată

>>> N = 10 >>> defaddN (i, n = N). returnați i + n. >>> addN (5) # Adăugați 10 15 >>> N = 20 >>> addN (6) # Adăugați 10 (actual N nu contează) 16

Variabila înghețată, în fapt, este închiderea. Unele date sunt atașate funcției addN (). În cazul închiderii complete, toate datele prezente la momentul descrierii acestei funcții ar fi disponibile atunci când a fost apelată. Cu toate acestea, în acest exemplu (și în multe altele mai grave), puteți oferi pur și simplu acces la date suficiente cu parametrii impliciți. La urma urmei, variabilele care nu sunt utilizate de funcția addN (). nu joacă nici un rol în calculul său.

Să analizăm abordarea obiectului cu un exemplu de problemă puțin mai presantă. În acest moment al anului, gândurile sunt de obicei ocupate de programe interactive utilizate pentru a calcula impozitul. Ei colectează date diferite - nu neapărat într-o anumită ordine - și apoi la un moment dat le folosesc la calcul. Să creăm o versiune simplificată a unui astfel de program:

Listing 7. Clasa Python pentru calcularea impozitului

Clasa de TaxCalc: deftaxdue (auto): întoarcere (self.income-self.deduct) * self.rate taxclass = TaxCalc () taxclass.income = 50000 taxclass.rate = 0,30 taxclass.deduct = 10000 imprimare "taxe pythonic OOP datorate =" , taxclass.taxdue ()

În clasa noastră TaxCalc (mai exact, în cazul său), putem colecta date - în orice ordine - și de îndată ce avem toate elementele necesare, putem apela metoda obiect pentru a efectua calculul asupra datelor colectate. Totul este colectat în cadrul instanței și, prin urmare, diferite instanțe pot specifica seturi de date diferite. Crearea unui set de instanțe care diferă una de alta numai cu datele lor este imposibilă atunci când se utilizează variabile globale sau "înghețate". Abordarea "forță brută" poate rezolva această problemă, dar puteți vedea că, într-un exemplu real, este posibil să trebuiască să transferați mai multe valori. Acum, să vedem cum puteți rezolva această problemă cu OOP utilizând transmiterea mesajelor (aceasta este similară cu Smalltalk sau Self, precum și cu unele din variantele XBase orientate pe obiecte pe care le-am folosit):

Listing 8. Calcularea impozitului în stilul Smalltalk (Python)

Clasa de TaxCalc: deftaxdue (auto): întoarcere (self.income-self.deduct) * self.rate defsetIncome (auto, venituri): întoarcere self.income = venit de sine defsetDeduct (auto, deducere): self.deduct = deducere a reveni auto defsetRate (auto, rata) :. self.rate = rata de rentabilitate de sine de imprimare "taxe de tip Smalltalk datorate =", \ TaxCalc () setIncome (50000) .setRate (0,30) .setDeduct (10000) .taxdue ()

Returul de sine de către fiecare instalator ne permite să tratăm instanța curentă ca urmare a apelării fiecărei metode. Așa cum se poate observa în continuare, această abordare are caracteristici interesante în comun cu utilizarea unei închideri într-o tranziție de fază.

Colaborând cu setul de instrumente Xoltar, puteți crea închideri complete care au caracterul necesar de combinare a datelor cu o funcție, precum și închideri multiple care au diferite seturi de date:

Listing 9. Calcule fiscale stil Python

de la funcțional import * taxdue = lambda. (Venituri-deducere) * Rata incomeClosure = venit lambda, taxdue: închidere (taxdue) deductClosure = lambda deduce, taxdue: închidere (taxdue) rateClosure = rata lambda, taxdue: închidere (taxdue) taxFP = taxdue taxFP = incomeClosure (50000, taxFP ) taxFP = rateClosure (0,30, taxFP) taxFP = deductClosure (10000, taxFP) print "taxe funcționale datorate =", taxFP () print "taxe de tip Lisp datorate =", \ incomeClosure (50000, rateClosure (0,30, deductClosure (10.000 , taxe))))

Fiecare funcție de închidere descrisă de noi ia orice valoare definită în domeniul său de aplicare și leagă aceste valori de domeniul global al obiectului funcțional. Cu toate acestea, ceea ce pare a fi domeniul de aplicare global al acestei funcții nu coincide neapărat atât cu domeniul "real" global al modulului, cât și cu scopul global al unei alte închideri. Închiderea pur și simplu "poartă date cu tine".

În exemplul nostru, pentru a plasa anumite valori în scopul închiderii, folosim câteva funcții specifice (venit, deducere, rată). Ar fi suficient să schimbați pur și simplu designul astfel încât să puteți atribui valori arbitrare. În plus, de dragul divertismentului, folosim în acest exemplu două stiluri funcționale puțin diferite. Primul leagă secvențial valori adiționale zonei de defect; Efectuând modificarea taxFP, lăsăm rândurile adăugate la închidere să apară în orice ordine. Cu toate acestea, dacă am folosit nume imuabile precum tax_with_Income. Va trebui să aranjăm legăturile într-o anumită ordine și să le transferăm pe cele anterioare la următoarea. În orice caz, de îndată ce tot ceea ce este necesar este legat de o închidere, putem numi funcția crescută.

Al doilea stil, în opinia mea, este oarecum similar cu Lisp (numai din cauza prezenței parantezelor). În plus față de estetică, în al doilea stil există două momente interesante. În primul rând, nu există legare a numelor. Al doilea stil este o singură expresie fără a utiliza directive. (a se vedea articolul precedent, care explică de ce acesta joacă un rol).

Un alt detaliu interesant al stilului Lisp este cât de mult folosirea închiderilor se aseamănă cu metodele de trimitere a mesajelor a la Smalltalk, menționate mai sus. În ambele cazuri, valorile se acumulează înainte de a apela taxiul () pentru funcție / metodă (ambele în aceste versiuni simplificate ridică excepții dacă datele solicitate nu sunt disponibile). Stilul smalltalk trece un obiect la fiecare pas, în timp ce stilul Lisp este o continuare. Dar, dacă vă uitați la rădăcină, atunci programarea funcțională și orientată spre obiect duce la aproape același lucru.

Recurgerea taurului

Descărcați resurse

Subiecte conexe







Trimiteți-le prietenilor: