Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web servers, web development, networking and security services. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Funkcionalno programiranje u PHP-u

Funkcionalno programiranje u PHP-u

PHP je tradicionalno bio jednostavni, proceduralni jezik koji je mnogo inspiracije vukao iz C-a i Perl-a. Podjednako mudrost sintakse i sigurnost poptisa funkcija su komplikovani koliko je moguće.PHP 5.0 je predstavio odgovarajući model objekta,ali vi sve to već znate.PHP 5.3 je uveo zaključke,a PHP 5.4 u velikoj meri poboljšao zaključke (nagoveštaj: $ovo je dostupno po default-u).

Šta je uopšte funkcionalno programiranje?

Nakon nekoliko godina uvođenja sve više funkcionalnih elemenata u moj source kod,nije jednostavno odgovoriti. Još uvek nemam povezanu definiciju,ali “znaću je kada je vidim”. Da kažem ovako: funkcionalni programi generalno ne menjaju stanje,ali koriste čiste funkcije. Čiste funkcije uzimaju jednu vrednost i vraćaju drugu bez promene svog input argumenta. Suprotni primer je tipično podešavanje u objektno orijentisanom kontekstu.

Tipični jezici funkcionalnog programiranja podržavaju funkcije višeg reda,to jest,funkcije koje uzimaju ili vraćaju druge funkcije. Mnoge od njih podržavaju koncept koji se zove izglađena ili parcijalna aplikacija funkcije (PFA).

Dodatne karakteristike pronađene u jezicima funkcionalnog programiranja su elaborirani tipski sistemi koji npr. koriste opciju types da spreče probleme sa pokazivačem nule tipičnim za imperativne ili objektno orijentisane programske jezike.

Funkcionalno programiranje ima puno poželjnih atributa: ne petljanje sa stanjem olakšava paralelizam (nije lako,nikada nije lako), fokusiranje na najmanju jedinicu koda za višekratnu upotrebu,funkciju,može odvesti do zaista interesantnih efekata sa obzirom na mogućnost višekratne upotrebe,zahtevanje da funkcije budu odlučne je uošte dobra ideja za stabilan software.

Šta PHP ima da ponudi?

PHP nije “pravi” ili “čisti” funkcionalni jezik. Daleko od toga. Mi nemamo odgovarajući tipski sistem,kul klinci ismevaju našu egzotičnu sintaksu za zaključke i imamo array_walk() koji izgleda funkcionalno,ali dopušta promenu stanja.

I pored toga,postoji nekoliko interesantnih blokova izgradnje za funkcionalno programiranje. Počnimo sa call_user_func,call_user_func_array i $callable(). call_user_func uzima povratni poziv i listu argumenata i priziva dati povratni poziv sa datim argumentima. To je gotovo isto kao fn.call() i fn.apply() u JavaScrip-ut (bez prelaženja obima). Manje poznata nova sjajna stvar u PHP 5.4 je sposobnost pozivanja funkcija direktno na opozive (callables). callable je meta tip u PHP-u (kao u:sastoji se od različitih osnovnih tipova): callable može biti string za pozivanje jednostavnih funkcija, array (niz) <string,string> za pozivanje statičkih metoda, i array <object,string> za pozivanje objektnih metoda, neka instanca Closure-a (zaključak) ili bilo čega što implementira __invoke() magičnu matodu, znanu takođe i kao Functor. Ovo će izgledati ovako:

$print = 'printf';
$print("Hello %s\n", 'World');

Dodatno tome, PHP 5.4 uvodi novi tipski nagoveštaj “callable”koji obezbeđuje jednostavan sklop za callable meta tip.

PHP takođe podržava anonimne funkcije. Kao što sam ranije rekao, Haskell je sintaksa nekako preopširna. Pogledajmo prosti primer Python-a.

map(lambda v: v * 2, [1, 2, 3])

Lepo.Pogledajmo mali primer Ruby-a:

[1, 2, 3].map{|x| x * 2}

Isto lepo,mada mi ovde koristimo blok,a ne striktno lambda izraz. I Ruby ima lambdu,ali je slučaj da List.map uzima blok,a ne funkciju. Sledeći primer je Scala:

List(1, 2, 3).map((x: Int) => x * 2)

Za striktno tipizirani jezik,to je prilično kompaktno.Sada gledajte PHP:

array_map(function ($x) {return $x * 2;}, [1, 2, 3]);

Ključna reč funkcije i ne-implicitni povraćaj su ono što čini da izgleda prilično nezgrapno. Ali u svakom slučaju,radi. Još jedan blok za izgradnju za funkcionalno programiranje.

array_map je dobar početak, tu je i array_reduce. Još dve važne funkcije.

Funkcionalni primer iz stvarnog sveta

Počnimo sa jednostavnim programom da izračunamo ukupne vrednosti u kolicima za kupovinu:

$cart = [
    [
        'name'     => 'Item 1',
        'quantity' => 10,
        'price'    => 9.99,
    ],
    [
        'name'     => 'Item 2',
        'quantity' => 3,
        'price'    => 5.99,
    ]
];
 
function calculate_totals(array $cart, $vatPercentage)
{
    $totals = [
        'gross' => 0,
        'tax'   => 0,
        'net'   => 0,
    ];
 
    foreach ($cart as $position) {
        $sum = $position['price'] * $position['quantity'];
        $tax = $sum / (100 + $vatPercentage) * $vatPercentage;
        $totals['gross'] += $sum
        $totals['tax'] += $tax
        $totals['net'] += $sum - $tax; 
    }
 
    return $totals;
}
 
calculate_totals($cart, 19);

Da,to je jednostavan primer koji će funkcionisati samo za jedinstveno tržište,ali je to upola komlikovana kalkulacija i mi lako možemo promeniti koeficijent u funkcionalniji stil.

Koristimo prvo funkcije višeg reda:

$cart = [
    [
        'name'     => 'Item 1',
        'quantity' => 10,
        'price'    => 9.99,
    ],
    [
        'name'     => 'Item 2',
        'quantity' => 3,
        'price'    => 5.99,
    ]
];
 
function calculate_totals(array $cart, $vatPercentage)
{
   $cartWithAmounts = array_map(
       function (array $position) use ($vatPercentage) {
           $sum = $position['price'] * $position['quantity'];
           $position['gross'] = $sum;
           $position['tax'] = $sum / (100 + $vatPercentage) * $vatPercentage;
           $position['net'] = $sum - $position['tax'];
           return $position;
       },
       $cart
   );
 
   return array_reduce(
       $cartWithAmounts,
       function ($totals, $position) {
           $totals['gross'] += $position['gross'];
           $totals['net'] += $position['net'];
           $totals['tax'] += $position['tax'];
           return $totals;
       },
       [
           'gross' => 0,
           'tax'   => 0,
           'net'   => 0,
       ]
   );
}
 
calculate_totals($cart, 19);

Sada više ne menjamo stanje,čak ni u samoj funkciji. array_map() vraća novi array(niz) pozicija kolica sa bruto,poreskim,neto iznosima i redukcija niza sastavlja niz ukupnih vrednosti. Ali možemo li ići dalje? Možemo li ga učiniti puno jednostavnijim?

Šta ako dalje destrukturiramo program i apstrahujemo ga u ono što on zaista radi:
* Saberite element jednog niza pomnoženog sa još jednim elementom * Oduzmite procenat tog zbira * Izračunajte razliku između procenta i zbira

Sada nam treba mali pomagač. Taj mali pomagač je functional-php , mala biblioteka funkcionalnih “primitivaca”koje razvijam sada već nekoliko godina. Prvo,postoji Functional\pluck() koji radi isto što i _.pluck() iz underscore.js-a. Druga funkcija od pomoći je Functional\zip(). Ona “zip-uje” zajedno dve liste,opciono koristeći callback (povratni poziv). Functional\sum() sabira elemente liste.

use Functional as F;
$cart = [
    [
        'name'     => 'Item 1',
        'quantity' => 10,
        'price'    => 9.99,
    ],
    [
        'name'     => 'Item 2',
        'quantity' => 3,
        'price'    => 5.99,
    ]
];
 
function calculate_totals(array $cart, $vatPercentage)
{
    $gross = F\sum(
        F\zip(
            F\pluck($cart, 'price'),
            F\pluck($cart, 'quantity'),
            function($price, $quantity) {
                return $price * $quantity;
            }
        )
    );
    $tax = $gross / (100 + $vatPercentage) * $vatPercentage;
 
    return [
        'gross' => $gross,
        'tax'   => $tax,
        'net'   => $gross - $tax,
    ];
}
 
calculate_totals($cart, 19);

Drugi kontra argument je: da li je to zaista lakše za čitanje. Kao prvo: ne,na drugi pogled: navićićete se. Trebalo mi je vremena da se naviknem na sintaksu Scala-e,trebalo je vremena da naučim objektno orijentisano programiranje i treba vremena da ovladam funkcionalnim programima. Da li je ovo savršeno rešenje? Ne. Ali pokazujr šta možete da uradite ako više razmišljate u pogledu primene funkcija na strukturu podataka umesto da koristite izraze kao foreach da upravljate radom na strukturama podataka.

Šta drugo možemo da uradimo?

Da li ste ikada imali problema sa izuzecima pokazivača nule (null pointer)? Postoji php-option koja obezbeđuje implementaciju polimorfnog “tipa možda” korišćenjem PHP objekta.

Onda je tu parcijalna aplikacija: ona transformkoja uzima n parametre za funkciju koja uzima <n parametre. Zašto je toliko od pomoći? Razmislite o ekstrahovanju prvog karaktera iz liste string-ova.

Dosadni način:

$list = ['foo', 'bar', 'baz'];
$firstChars = [];
foreach ($list as $str)  {
    $firstChars[] = substr($str, 0, 1);
}

Funkcionalni način bez PFA (partial function application):

array_map(function ($str) {return substr($str, 0, 1);}, ['foo', 'bar', 'baz']);

Način sa PFA korišćenjem reactphp/curry (moja omiljena currying (izglađena) implementacija za PHP):

use React\Curry;
array_map(Curry\bind('substr', Curry\…(), 0, 1), ['foo', 'bar', 'baz']);

Da. … (HORIZONTAL ELLIPSIS, U+2026) je validno ime funkcije u PHP-u. Ali ako vam se to ne sviđa, koristite Curry\placeholder() umesto toga.

Kraj

Funkcionalno programiranje je fascinantna tema i morao sam da imenujem jednu stvar koju sam najviše iz njega naučio tokom poslednjih godina,a to je bilo gledanje u funkcionalne paradigme. Toliko je različito da će vas zaboleti mozak. Ali na dobar način. A, još jedna stvar: pročitajte Real World Functional Programming (Funkcionalno programiranje realnog sveta) . Puno je dobrih saveta i primera iz realnog sveta.

Update

Hvala Vam, Christopher Jones za sređivanje primera funkcije višeg reda (drugi korak).

Update II

Hvala Vam, Anthony Ferrara za ukazivanje da je primer array_map-a bio pogrešan. Mora da obožavate uređivanje parametara.

Update III

Postoji ruski prevod .





Published (Last edited): 16-05-2013 , source: http://usrportage.de/archives/941-Functional-programming-in-PHP.html