U računalnom programiranju, anonimna funkcija (funkcijski literal, lambda apstrakcija, lambda funkcija, lambda izraz ili blok) je definicija funkcije koja nije vezana uz identifikator (nema ime). Anonimne funkcije često služe kao argumenti koji se prosljeđuju funkcijama višeg reda ili se koriste za izgradnju rezultata takvih funkcija.

Ako se funkcija koristi samo jednom ili ograničeni broj puta, anonimna funkcija je sintaktički lakša od imenovane funkcije.

Anonimne funkcije se stalno koriste u funkcijskom programiranju i jezicima koji podržavaju prvorazredne funkcije, pa se smatraju svojevrsnim nasljednicima funkcijskih objekata.

Anonimne funkcije prvi se put pojavljuju 1936. godine u radovima Alonza Churcha pri njegovom izumu lambda računa, u kojem su sve funkcije anonimne..

Nekolicina programskih jezika koristi ključnu riječ lambda pri deklaraciji anonimnih funkcije, pa se anonimne funkcije često nazivaju lambdama, lambda funkcijama ili lambda apstrakcijama.

Prvi jezik koji je uveo lambde je Lisp 1958. godine, a podržava ih sve više modernih programskih jezika.

Imena Imena lambda apstrakcija, lambda funkcija i lambda izraz odnose se na zapis funkcijske apstrakcije u lambda računu, gdje bi se funkcija zapisala kao (M je izraz koji koristi x). Sintaksa lambda funkcija u jeziku Python vrlo je slična ovome:lambda x: M.

Ime arrow funkcija odnosi se na matematički simbol mapiranja (maplet) . JavaScript i Java koriste sličnu sintaksu: x => M.

Upotreba Korištenje anonimnih funkcija dio je programerskog stilskog izražaja - funkcionalnost pojedine anonimne funkcije može se ostvariti i običnom, imenovanom funkcijom. Neki programeri koriste anonimne funkcije za enkapsulaciju specifičnog koda koji neće biti korišten na više mjesta kako bi poboljšali čitljivost programskog koda.

U nekim programskim jezicima, anonimne se funkcije koriste u specifične svrhe poput vezanja događaja na povratne pozive (callback) ili instanciranje neke funkcije za specifične parametarske vrijednosti, što je efikasnije, čitljivije i manje sklono pogreškama od pozivanja generičkih imenovanih funkcija.

Sortiranje Sljedeći su primjeri pisani u Pythonu 3.

Pri sortiranju nestandardnih elemenata, logiku sortiranja ponekad je lakše zadržati u anonimnoj funkciji. Većina programskih jezika pruža generičku funkciju sortiranja koja implementira algoritam za sortiranje sposoban sortirati željene elemente. Generička funkcija za sortiranje obično prima ručno napisanu funkciju koja određuje način usporedbe dvaju elemenata za potrebe sortiranja.

Sljedeći primjer sortira elemente po duljini teksta:

>>> a = ['automobil', 'kuća', 'bicikl'] >>> a.sort(key=lambda x: len(x)) >>> a ['kuća', 'bicikl', 'automobil']

Anonimna funkcija u gornjem primjeru je lambda izraz: lambda x: len(x) Anonimna funkcija prihvaća jedan argument, x, i vraća njegovu duljinu, što metoda sort() zatim koristi kao kriterij sortiranja.

Osnovna sintaksa lambda funkcije u Pythonu je: lambda arg1, arg2, arg3, ...: <operacija nad argumentima koja vraća vrijednost> Vrijednost koju lambda izraz vrati može se dodijeliti varijabli i koristiti u daljnjem kodu na više mjesta. >>> zbroj = lambda a: a + a >>> zbroj(20) 40

Stavke popisa mogu se također sortiratu prema imenu njihove klase:

>>> a = [10, 'broj', 11.2] >>> a.sort(key=lambda x: x.__class__.__name__) >>> a [11.2, 10, 'broj']

Stavka 11.2 dio je klase imena "float", 10 ima klasu imena "int", dok je 'broj' dio klase "str". Redoslijed sortiranja je stoga "float", "int", i naposljetku "str".

C++ Jezik C++ (standard C++11) koristi specifičnu sintaksu lambda funkcija kojom su eksplicitno naglašene vezane varijable: Kod ekvivalentan gornjem primjeru zbroja, koji koristi direktno prosljeđivanje vrijednosti putem parametara, može se napisati ovako: #include <iostream> using namespace std;

int main() { int a = 1, b = 2; auto zbroj = [](int x, int y) -> int { return x + y; }; cout << zbroj(a, b) << endl; return 0; } Potpis gornje funkcije (tip auto) pri prevođenju poprima stvarnu vrijednost std::function<int(int, int)> (funkcijski tip).

U gornjem kodu, [] je , koja definira kojim varijablama lambda ima pristup i na koji se način iste prenose. Klauzula uvijek mora biti definirana ali može biti prazna.

Ako umjesto parametara koristimo klauzulu, kod izgleda ovako: #include <iostream> using namespace std;

int main() { int a = 1, b = 2; auto zbroj = [a, b]() -> int { return a + b; }; cout << zbroj() << endl; return 0; } Ovdje nije potrebno tipizirati ni prosljeđivati a i b jer su vrijednosti klauzulom hvatanja preuzete iz okolnog koda. Prilikom takvog poziva, kompilator generira anonimni funkcijski objekt preopterećenjem operatora ( ), koji izravno pristupa varijablama a i b, prenosi ih po vrijednosti (pass-by-value), te lambda objekt generira njihove konstantne kopije.

Klauzuli nije potrebno prosljeđivati globalne ni statičke varijable jer su one automatski dostupne u opsegu lambda funkcije.

Povratni tip lambda funkcije (->int) također nije potrebno definirati ukoliko se unutar lambde izvršava samo jedna naredba.

Kako bismo mogli promijeniti vrijednosti lokalnih kopija uhvaćenih varijabli, potrebno je dodati deklaraciju mutable, ali promjene vrijednosti opstaju samo u lokalnom opsegu lambde: #include <iostream> using namespace std;

int main() { int a = 1, b = 2; auto zbroj = [a, b]() mutable -> int { a=15; // bez 'mutable' ovdje izlazi greška ' error: assignment of read-only variable' return a + b;

cout << zbroj() << '=' << a << '+' << b << endl; // ispisuje "17=1+2" return 0; }

Klauzulom hvatanja može se definirati i vrsta prijenosa svake varijable: & prije imena varijable prenosi istu po referenci, dok = prenosi po vrijednosti (zadano ponašanje). Prenošenjem varijable a po referenci, promjena njene vrijednosti unutar lambde opstaje po završetku lambda izraza. #include <iostream> using namespace std;

int main() { int a = 1, b = 2; auto zbroj = [&a, b]() mutable -> int { a=15; // bez 'mutable' ovdje izlazi greška ' error: assignment of read-only variable' return a + b;

cout << zbroj() << '=' << a << '+' << b << endl; // ispisuje "17=15+2" return 0; }

Kompilator može i sam zaključiti koje varijable prenosi, ako mu je zadan način prijenosa. Kompilator ovdje sam prenosi sve varijable koje lambda koristi (a i b) po referenci. #include <iostream> using namespace std; int main() { int a = 1, b = 2; auto zbroj = [&]() mutable -> int { a = 15; return a + b;

cout << zbroj() << '=' << a << '+' << b << endl; return 0; }

Lambde se, kao i obične funkcije, također mogu pozivati rekurzivno, no potrebno je izrazom function (u zaglavlju <functional>) definirati delegat funkcije: #include <iostream> #include <functional> using namespace std; int main() { function<int(int, int)> zbroj = [&zbroj](int a, int b) -> int{ if (a > b) return 0; return a + zbroj(a + 1, b);

cout << zbroj(1, 5); // 1+2+3+4+5=15 return 0; }

Java Jezik Java od verzije 8 putem lambda izraza podržava sekvencijalne i paralelne agregatne operacije nad objektima (obično kolekcijama).

S tom je svrhom uvedeno generičko sučelje stream, koje dopušta deklarativno opisivanje operacija nad elementima niza.

Agregatne operacije započinju metodom .stream() nakon koje slijedi jedna ili više ulančanih metoda koje primaju lambda izraze i transformiraju podatke jedan po jedan, i naposljetku isključivo jedna završna (terminalna) operacija koja vraća rezultat. Završna operacija služi kao pokretač procesiranja, uzima jedan po jedan element iz kolekcije, i slijedno ga provodi kroz sve ulančane metode.

Najčešće ulančane metode su filter(…) - predikatna funkcija (vraća true ili false) koja zadržava samo elemente koji udovoljavaju uvjetu map(…) - transformira svaki od elemenata

Najčešće agregatne metode su count() - vraća količinu elemenata sum() - vraća zbroj elemenata average() - vraća prosjek elemenata max() - vraća najveću vrijednost min() - vraća najmanju vrijednost findFirst() - vraća prvi element reduce(…) - općenita operacija redukcije po pruženom parametru collect(…) - skuplja sve elemente u kolekciju definiranu parametrom (lista, niz, set ili slično)

Agregatne metode obično vraćaju tip podatka Optional<T> kao osiguranje za slučaj neispravnog rezultata (primjerice prosjek niza od 0 elemenata). Završna operacija takvih agregata može biti primjerice .getAsInt() koja vraća 0 umjesto iznimke u slučaju neispravnog rezultata. Također postoje terminalne operacije koje mogu vratiti predefinirani rezultat u slučaju pogreške, primjerice .OrElse(…).

List<Integer> brojevi = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println("Originalni brojevi: " + brojevi);

List<Integer> parni = brojevi .stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList());

System.out.println("Parni brojevi: " + parni); //ispisuje "2, 4, 6, 8, 10"

U gornjem kodu metoda .filter() zadržava samo elemente koji zadovoljavaju uvjetima lambda funkcije. Primjerice, gornji kod .filter(n -> n % 2 == 0) uzima jedan po jedan element liste, dodjeljuje ga varijabli n i provjerava je li ostatak dijeljenja broja n sa 2 jednak 0 (n -> n % 2 == 0). Ako jest, element se zadržava i nastavlja ulančavanje. Ako nije, odbacuje se, i preuzima se sljedeći element.

Završna operacija (.collect(Collectors.toList())) stvara anonimnu listu cijelih brojeva koji su došli do zadnjeg koraka, i dodjeljuje ju varijabli imena parni.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Integer max = numbers .stream() .filter(n -> n > 3) .mapToInt(n -> n * 2) .max() .orElse(0);

System.out.println("Najveća vrijednost: " + max); //ispisuje "10" Gornji kod uzima elemente liste numbers, zadržava samo elemente veće od 3, množi svaki element sa 2 i vraća najveći rezultat. U slučaju da nijedan element nije prošao filtriranje, vraća se rezultat 0.

Izvori

Kategorija:Konstrukti u programiranju