Funkcja anonimowa
Funkcja anonimowa (również literał funkcyjny lub lambda-abstrakcja) – definicja funkcji, która sama w sobie nie jest powiązana z identyfikatorem (chociaż może być przypisana do zmiennej).
Funkcje anonimowe mogą być[1]:
- argumentami przekazywanymi funkcjom wyższego rzędu lub
- używane do budowania wyniku funkcji wyższego rzędu, która musi zwracać funkcję.
Jeśli funkcja jest używana tylko jeden raz lub ograniczoną liczbę razy, użycie funkcji anonimowej może być syntaktycznie wygodniejsze niż użycie funkcji nazwanej. Funkcje anonimowe są wszechobecne w funkcyjnych językach programowania i innych językach z obecnymi funkcjami pierwszego rzędu, gdzie pełnią taką samą rolę dla typu funkcji jak literały dla innych typów typów danych.
Funkcje anonimowe biorą początek w pracach Alonzo Churcha nad wynalezieniem rachunku lambda w 1936 roku (przed pojawieniem się komputerów elektronicznych), w którym wszystkie funkcje są anonimowe[2]. Przejawia się to między innymi tym, że w niektórych językach programowania, funkcje anonimowe są tworzone faktycznie przez użycie słowa kluczowego lambda. Ponadto funkcje anonimowe są często nazywane lambdami lub lambda-abstrakcjami. Funkcje anonimowe to jedna z charakterystycznych cech języków programowania od czasu powstania języka Lisp w 1958 roku i coraz większa liczba nowoczesnych języków programowania wspiera funkcje anonimowe.
Funkcje anonimowe są rodzajem funkcji zagnieżdżonych, zapewniając dostęp do zmiennych w zasięgu funkcji zawierającej (zmiennych nielokalnych). Oznacza to, że anonimowe funkcje powinny być implementowane z wykorzystaniem domknięć. W przeciwieństwie do nazwanych funkcji zagnieżdżonych, nie mogą one być rekurencyjne bez zastosowania operatora punktu stałego (również znanego jako anonimowe mocowanie lub anonimowa rekursja) lub przypisywania ich do nazwy[3].
Zastosowanie
[edytuj | edytuj kod]Funkcje anonimowe mogą być wykorzystane, by zawierać w sobie funkcjonalności, które nie powinny być nazwane oraz, być może mają jedynie krótkotrwały użytek. Niektóre przykłady, o których warto wspomnieć, obejmują domknięcia i currying.
Funkcje anonimowe to kwestia stylu programowania. Ich stosowanie nie jest wymagane. W dowolnym miejscu, w którym można ich użyć, można także zdefiniować specjalną nazwaną funkcję, która działa dokładnie tak samo. Niektórzy programiści wykorzystują funkcje anonimowe dla hermetyzacji niektórych fragmentów kodu, w ten sposób odbierając możliwość ponownego wykorzystania, co zapobiega zaśmiecaniu kodu wieloma małymi jednowierszowymi nazwanymi funkcjami.
W niektórych językach programowania, można określić anonimową funkcję wykonaną dla specjalnego zastosowania, aby dać właśnie to (i tylko), czego chce programista, co jest bardziej wydajne, eleganckie i mniej podatne na błędy pewnych operacji, które korzystają ze stałych wartości.
Cały kod w następujących sekcjach został napisany języku Python 2.x (nie 3.x).
Sortowanie
[edytuj | edytuj kod]Podczas próby sortowania w niestandardowy sposób może być łatwiej zawrzeć logikę porównania w funkcji anonimowej, niż tworzyć funkcję nazwaną. Większość języków zapewniają generyczną funkcję sortowania, która realizuje algorytm sortowania, który może sortować obiekty dowolnego typu. Funkcja ta zwykle przyjmuje dowolną funkcję porównującą, która przyjmuje dwa argumenty i określa, czy są równe lub czy pierwszy jest „większy” bądź „mniejszy” od drugiego (zwykle zwracając liczbę ujemną, zero lub dodatnią).
Rozważmy sortowanie listy łańcuchów po długości poszczególnych elementów:
>>> a = ['dom', 'samochód', 'rower']
>>> a.sort(lambda x,y: cmp(len(x), len(y)))
>>> print(a)
['samochód', 'rower', 'dom']
Funkcja anonimowa w tym przykładzie to wyrażenie lambda:
lambda x,y: cmp(...)
Anonimowa funkcja przyjmuje dwa argumenty: x
i u=y
oraz zwraca ich porównanie, korzystając z wbudowanej funkcji cmp()
.
Inny przykład to sortowanie listy elementów według nazw ich klas (w języku Python, wszystko ma klasę):
>>> a = [10, 'liczba', 11.2]
>>> a.sort(lambda x,y: cmp(x.__class__.__name__, y.__class__.__name__))
>>> print(a)
[11.2, 10, 'liczba']
Należy pamiętać, że nazwa klasy 11.2
to „float
”, nazwa klasy 10
to „int
”, i nazwa klasy 'liczba'
to „str
”. W posortowanej kolejności-to „float
”, „int
”, a potem „str
”.
Domknięcia
[edytuj | edytuj kod]Domknięcia to funkcje ewaluowane w środowisku zawierające zmienne o ustalonych wartościach. Poniższy przykład przypisuje „wartość graniczną (threshold)” zmiennej w funkcji anonimowej, która porównuje wejście do argumentu threshold.
def comp(threshold):
return lambda x: x < threshold
Może to być używane jako swego rodzaju generator funkcji porównania:
>>> func_a = comp(10)
>>> func_b = comp(20)
>>> print func_a(5), func_a(8), func_a(13), func_a(21)
True True False False
>>> print func_b(5), func_b(8), func_b(13), func_b(21)
True True True False
Byłoby niepraktyczne tworzenie funkcji nazwanej dla każdej możliwej funkcji porównującej, a zachowywanie wartości granicznej dla przyszłych zastosowań byłoby absolutnie nieeleganckie. Niezależnie od przyczyny zastosowanie domknięcia funkcja anonimowa to jednostka, która zawiera funkcjonalność wykonywania porównania.
Currying
[edytuj | edytuj kod]Currying to proces modyfikacji funkcji tak, aby przyjmowała mniejszą ilość argumentów (w tym przypadku, transformujemy funkcję, która wykonuje dzielenie przez liczbę całkowitą, w taką która wykonuje dzielenie przez jeden, ustalony dzielnik).
>>> def dziel(x, y):
... return x / y
>>> def dzielenie(d):
... return lambda x: dziel(x, d)
>>> naPol = dzielenie(2)
>>> naTrzy = dzielenie(3)
>>> print naPol(32), naTrzy(32)
16 10
>>> print naPol(40), naTrzy(40)
20 13
Podczas gdy korzystanie z anonimowych funkcji razem z curryingiem nie jest może zbyt powszechne, to wciąż może być stosowane. W powyższym przykładzie funkcja dzielenie generuje funkcję dzielącą przez określony dzielnik. Funkcje naPol oraz naTrzy tworzą funkcje podziału o stałych dzielnikach.
Funkcja dzielenie tworzy również domknięcie poprzez przypisanie zmiennej „d” wartości.
Funkcje wyższego rzędu
[edytuj | edytuj kod]Python 2.x zawiera kilka funkcji, które pobierają jako argument anonimowe funkcje. W tej sekcji opisano niektóre z nich.
Map
[edytuj | edytuj kod]Funkcja Map wykonuje wywołanie funkcji dla każdego elementu listy. W poniższym przykładzie podnosi do kwadratu każdy element tablicy, korzystając z funkcji anonimowej.
>>> a = [1, 2, 3, 4, 5, 6]
>>> print map(lambda x: x*x, a)
[1, 4, 9, 16, 25, 36]
Anonimowa funkcja przyjmuje argument, który mnoży z samym sobą (podnosi go do kwadratu).
Filter
[edytuj | edytuj kod]Funkcja filter zwraca wszystkie elementy z listy, które przekazane jako argument do określonej wcze��niej funkcji wartościują ją na True.
>>> a = [1, 2, 3, 4, 5, 6]
>>> print filter(lambda x: x % 2 == 0, a)
[2, 4, 6]
W powyższym przykładzie anonimowa funkcja sprawdza, czy przekazany mu argument jest parzysty.
Fold
[edytuj | edytuj kod]Funkcje fold/reduce (składanie/redukowanie) przechodzą po wszystkich elementach na liście (zwykle od lewej do prawej), nagromadzając je, w określony sposób, do pewnej wartości. Powszechne wykorzystywane są, aby zredukować wszystkie elementy listy do jednej wartości, np.:
>>> a = [1, 2, 3, 4, 5]
>>> print reduce(lambda x,y: x*y, a)
120
Co wykonuje
- .
Zaprezentowana tutaj funkcja anonimowa mnoży dwie wartości.
Wynikiem funkcji fold nie musi koniecznie być jedna wartość - właściwie rzecz biorąc funkcje map i filter mogą być zaimplementowane z użyciem fold. W przypadku funkcji map, wartość nagromadzona to nowa lista, zawierająca wyniki wywołania funkcji dla każdego elementu listy oryginalnej. W filter, wartość nagromadzona to nowa lista zawierająca tylko te elementy, które spełniają określony warunek.
Lista języków
[edytuj | edytuj kod]Poniżej znajduje się lista języków programowania, które w pełni obsługują nienazwane funkcje anonimowe, wspierają jakiś wariant funkcji anonimowych lub nie mają wsparcia dla funkcji anonimowych.
Ta tabela pokazuje pewne ogólne trendy. Po pierwsze, języki, które nie obsługują funkcji anonimowych – C, Pascal, Object Pascal – są konwencjonalnymi, statycznie pisanymi językami. To jednak nie oznacza, że statycznie wpisane języki są niezdolne do obsługi funkcji anonimowych. Na przykład, języki z rodziny ML są statycznie typowane, jednak fundamentalnie zawierają funkcje anonimowe, a język Delphi, dialekt Object Pascal, został rozbudowany o wsparcie funkcji anonimowych. Po drugie, języki które traktują funkcje jako funkcje pierwszego rzędu – Dylan, JavaScript, Lisp, Scheme, ML, Haskell, Python, Ruby, Perl – generalnie posiadają wsparcie dla funkcji anonimowych, co oznacza, że funkcje mogą być definiowane i przekazywane z taką samą łatwością jak inne typy danych. Gdyby ktoś nie uznawał powyższych przykładów za wystarczające, to wystarczy wspomnieć, że standard C++11 dodaje funkcje anonimowe do C++, czyli do języka konwencjonalnego, statycznie typowanego.
Język | Wsparcie | Notki |
---|---|---|
ActionScript | Tak | |
Ada | Nie | Wyrażenia funkcyjne są częścią Ada2012 |
ALGOL 68 | Tak | |
Brainfuck | Nie | |
Bash | Częściowo | Stworzono bibliotekę w celu dodania wsparcia dla funkcji anonimowych[4]. |
C | Nie | Wsparcie jest zapewniane w clang, wraz z llvm compiler-rt lib. Wsparcie w GCC jest udostępnione jako implementacja w makrach. Szczegóły zobacz niżej. |
C# | Tak | |
C++ | Tak | Część standardu C++11 |
CFML | Tak | Wraz z Railo 4[5] / ColdFusion 10[6] |
Clojure | Tak | |
COBOL | Nie | Niestandardowy dialekt języka Managed COBOL: Micro Focus wspiera lambdy, nazwane anonimowymi delegatami/metodami[7]. |
Curl | Tak | |
D | Tak | |
Dart | Tak | |
Delphi | Tak | |
Dylan | Tak | |
Eiffel | Tak | |
Erlang | Tak | |
F# | Tak | |
Factor | Tak | Wsparcie przez „Cudzysłowia”[8] |
Fortran | Nie | |
Frink | Tak | |
Go | Tak | |
Gosu | Tak | |
Groovy | Tak | |
Haskell | Tak | |
HaXe | Tak | |
Java | Tak | Wspierane od Java 8. |
JavaScript | Tak | Nazywane funkcjami strzałkowymi (ang. arrow functions) ze względu na swoją formę: (a, b) => a * b . Dostępne od ES2015.
|
Julia | Tak | |
Lisp | Tak | |
Logtalk | Tak | |
Lua | Tak | |
MUMPS | Nie | |
Mathematica | Tak | |
Maple | Tak | |
MATLAB | Tak | |
Maxima | Tak | |
OCaml | Tak | |
Octave | Tak | |
Object Pascal | Częściowo | Delphi, dialekt Object Pascal, implementuje natywne wsparcie dla anonimowych funkcji (formalnie metod anonimowych (ang. anonymous methods)) od konferencji Delphi 2009. Dialekt Oxygene również je wspiera. |
Objective-C (Mac OS X 10.6+) | Tak | Nazywane blokami; ponadto bloki Objective-C, mogą być wykorzystywane również w C and C++ gdy programujemy na platformie firmy Apple. |
Pascal | Nie | |
Perl | Tak | |
PHP | Tak | Od PHP 5.3.0 prawdziwe funkcje anonimowe są wspierane; wcześniej istniało tylko częściowe wsparcie dla funkcji anonimowych, które działało podobnie do implementacji w C#. |
PL/I | Nie | |
Python | Częściowo | Python wspiera funkcje anonimowe poprzez składnie lambda, w którym można używać tylko wyrażeń, nie pełnych instrukcji. |
R | Tak | |
Racket | Tak | |
Rexx | Nie | |
RPG | Nie | |
Ruby | Tak | Funkcje anonimowe Ruby, sprowadzone z języka Smalltalk, są nazywane blokami. |
Rust | Tak | |
Scala | Tak | |
Scheme | Tak | |
Smalltalk | Tak | Funkcje anonimowe są nazywane blokami. |
Standard ML | Tak | |
Swift | Tak | Są nazywane domknięciami (ang. Closures). |
TypeScript | Tak | |
Tcl | Tak | |
Vala | Tak | |
Visual Basic .NET v9 | Tak | |
Visual Prolog v 7.2 | Tak | |
Wolfram Language | Tak |
Przykłady
[edytuj | edytuj kod]Liczne języki obsługują funkcje anonimowe, lub podobną funkcjonalność.
C (niestandardowe rozszerzenia)
[edytuj | edytuj kod]Anonimowa funkcje nie są obsługiwane przez standardowy język C, ale są obsługiwane przez niektóre z jego dialektów, takie jak gcc i clang.
GCC
[edytuj | edytuj kod]GCC zapewnia wsparcie dla anonimowych funkcji, mieszanych przez funkcje zagnieżdżone i pewne wyrażenia. Ma postać:
( { zwracany_typ nazwa_funkcji_anonimowych (parametry) { cialo funkcji } nazwa_funkcji_anonimowych; } )
Poniższy przykład działa tylko z GCC. Należy również pamiętać, że ze względu na sposób działania makr, jeśli twoje l_body zawiera przecinki poza nawiasami, program się nie skompiluje, jako że GCC używa przecinka jako separatora do następnego argumentu w makro.
Argument l_ret_type
może być usunięty, jeśli __typeof__
jest dostępny. W przykładzie poniżej, użycie __typeof__
na tablicy zwróci wartość testtype *, która może być „wyłuskana”, aby uzyskać właściwą wartość, jeśli to konieczne.
# include <stdio.h>
//* to definicja funkcji anonimowej */
# define lambda(l_ret_type, l_arguments, l_body) \
({ \
l_ret_type l_anonymous_functions_name l_arguments \
l_body \
&l_anonymous_functions_name; \
})
# define forEachInArray(fe_arrType, fe_arr, fe_fn_body) \
{ \
int i=0; \
for(;i<sizeof(fe_arr)/sizeof(fe_arrType);i++) { fe_arr[i] = fe_fn_body(&fe_arr[i]); } \
}
typedef struct __test
{
int a;
int b;
} testtype;
void printout(const testtype * array)
{
int i;
for ( i = 0; i < 3; ++ i )
printf("%d %d\n", array[i].a, array[i].b);
printf("\n");
}
int main(void)
{
testtype array[] = { {0,1}, {2,3}, {4,5} };
printout(array);
/* funkcja anonimowa jest przekazana jako funkcja do foreach */
forEachInArray(testtype, array,
lambda (testtype, (void *item),
{
int temp = (*( testtype *) item).a;
(*( testtype *) item).a = (*( testtype *) item).b;
(*( testtype *) item).b = temp;
return (*( testtype *) item);
}));
printout(array);
return 0;
}
Clang (dla C, C++, Objective-C, Objective-c++)
[edytuj | edytuj kod]Clang zapewnia wsparcie dla funkcji anonimowych, zwanych blokami. Bloki mają formę:
^zwracany_typ ( parametry ) { cialo_funkcji }
Typ bloków powyżej to zwracany_typ (^)(parametery)
.
Wykorzystując powyższe bloki i libdispatch, kod mógłby wyglądać prościej:
# include <stdio.h>
# include <dispatch/dispatch.h>
int main(void) {
void (^count_loop)() = ^{
for (int i = 0; i < 100; i++)
printf("%d\n", i);
printf("ah ah ah\n");
};
/* Przekazanie jako parametr innej funkcji */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), count_loop);
/* Wywolaj bezposrednio */
count_loop();
return 0;
}
Kod z blokami musi być kompilowany z -fBlocksRuntime
i linkowany z -lBlocksRuntime
C++ (od C++11)
[edytuj | edytuj kod]C++11 oferuje wsparcie dla funkcji anonimowych nazywanymi wyrażeniami lambda. Lambda-wyrażenia mają postać:
[domknięcie](parametry) -> zwracany_typ { cialo_funkcji }
Przykładową funkcję lambda określa się w następujący sposób:
[](int x, int y) -> int { return x + y; }
C++11 obsługuje również domknięcia. Są określane w nawiasach kwadratowych [
i ]
w deklaracji wyrażenia lambda. Mechanizm pozwala tym zmiennym, aby były przekazywane przez wartość lub przez referencje. Poniższa tabela pokazuje różne warianty:
[] //brak zdefiniowanych zmiennych. Proba uzycia jakichkolwiek zewnetrznych zmiennych w lambdzie to blad.
[x, &y] //x jest przejmowana przez wartosc, y jest przejmowana przez referencje
[&] //jakakolwiek zmienna jest domyslnie przejmowana przez referencje, jesli zostaje uzyta
[=] //jakakolwiek zmienna jest domyslnie przejmowana przez wartosc, jesli zostaje uzyta
[&, x] //x jest jednoznacznie przejmowana przez wartosc. Inne zmienne domyslnie przez referencje
[=, &z] //z jest jednoznacznie przejmowana przez referencje. Inne zmienne domyslnie przez wartosc
Zmienne przekazane przez wartość są domyślnie traktowane jako stałe. Dodawanie mutable
po liście parametrów sprawia, że będą traktowane jako zmienne tymczasowe.
Następne dwa przykłady pokazują użycie wyrażeń lambda:
std::vector<int> some_list{ 1, 2, 3, 4, 5 };
int total = 0;
std::for_each(begin(some_list), end(some_list), [&total](int x) {
total += x;
});
Powyższy kod oblicza sumę wszystkich elementów listy. Zmienna total
jest przechowywana jako część domknięcia funkcji lambda. Jako że jest to referencja do zmiennej total
umieszczonej na stosie, funkcja może zmienić jej wartość.
std::vector<int> some_list{ 1, 2, 3, 4, 5 };
int total = 0;
int value = 5;
std::for_each(begin(some_list), end(some_list), [&, value, this](int x) {
total += x * value * this->some_func();
});
To spowoduje, że zmienna total
będzie przechowywana jako referencja, ale value
będzie przechowywana jako kopia.
Przekazywanie zmiennej this
jest wyjątkowe. Może być wykonane tylko przez wartość, nie przez referencję. this
może być przekazane tylko jeśli najbliższa funkcja zawierająca funkcję nie jest składową statyczną. Lambda będzie miała taki sam dostęp do składowych protected/private jak składowa, która ją utworzyła.
Jeśli this
zostaje przekazana, jawnie lub niejawnie, to zasięg otaczających składowych klasy także jest testowany. Dostęp do elementów this
nie wymaga jawnego użycia składni this->
.
Właściwa wewnętrzna realizacja może się różnić, ale oczekuje się, że funkcji lambda, do której wszystkie argumenty są przekazywane przez referencje, zachowa wskaźnik stosu funkcji, w której jest utworzona, zamiast indywidualnych referencji do zmiennych stosowych. Jednakże funkcje lambda, ze względu na małe wymiary i lokalność w ramach z zasięgu, są dobrymi kandydatami do zastosowania na nich metody inline, zatem nie będą wymagać dodatkowego miejsca na referencje.
Jeśli domknięcie, które zawiera referencje do zmiennych lokalnych, jest wywoływane po najniższym wewnętrznym zasięgu bloku, w którym zostało stworzone, to zachowanie jest niezdefiniowane.
Funkcje lambda to obiekty funkcyjne z typem zależnym od implementacji; nazwa tego typu jest dostępna tylko dla kompilatora. Jeżeli użytkownik życzy sobie pobrania funkcji lambda jako parametru, to jej typ musi być typem parametrycznym lub musi stworzyć std::function
lub podobny obiekt, aby uchwycić wartość lambdy. Korzystanie ze słowa kluczowego auto
może pomóc w zapisaniu funkcji lambda.
auto my_lambda_func = [&](int x) { /*...*/ };
auto my_onheap_lambda_func = new auto([=](int x) { /*...*/ });
Oto przykład przechowywania funkcji anonimowych w zmiennych, wektorach i macierzach; i przekazania ich jako nazwanych parametrów:
# include <functional>
# include <vector>
# include <iostream>
double eval(std::function <double(double)> f, double x = 2.0)
{
return f(x);
}
int main()
{
std::function<double(double)> f0 = [](double x){return 1;};
auto f1 = [](double x){return x;};
decltype(f0) fa[3] = {f0,f1,[](double x){return x*x;}};
std::vector<decltype(f0)> fv = {f0,f1};
fv.push_back ([](double x){return x*x;});
for(int i=0;i<fv.size();i++)
std::cout << fv[i](2.0) << std::endl;
for(int i=0;i<3;i++)
std::cout << fa[i](2.0) << std::endl;
for(auto &f : fv)
std::cout << f(2.0) << std::endl;
for(auto &f : fa)
std::cout << f(2.0) << std::endl;
std::cout << eval(f0) << std::endl;
std::cout << eval(f1) << std::endl;
std::cout << eval([](double x){return x*x;}) << std::endl;
return 0;
}
Wyrażenie lambda z pustym domknięciem ([]
) może być niejawnie przekształcone na wskaźnik na funkcję tego samego typu, jak typ, z którym lambda została zadeklarowana. Także to jest poprawne:
auto a_lambda_func = [](int x) { /*...*/ };
void (* func_ptr)(int) = a_lambda_func;
func_ptr(4); //wywoluje lambde
Biblioteka Boost udostępnia własną składnie dla funkcji lambda, korzystając z następującej składni:[9]
for_each(a.begin(), a.end(), std::cout << _1 << ' ');
C#
[edytuj | edytuj kod]Obsługa funkcji anonimowych w C# pogłębiała się w kolejnych wersjach kompilatora języka. Język C# v3.0, wydany w listopadzie 2007 roku z .NET Framework v3.5, cechuje się pełną obsługą funkcji anonimowych. Składnia C# odnosi się do nich jako „wyrażeń lambda”, zgodnie z pierwotnym nazewnictwem funkcji anonimowych w rachunku lambda. Zobacz Specyfikację Języka C# 4.0, sekcja 5.3.3.29, aby uzyskać więcej informacji.
// pierwszy int to typ x'
// drugi int to typ zwracanej wartosci
// <see href="http://msdn.microsoft.com/en-us/library/bb549151.aspx" />
Func<int,int> foo = x => x*x; Console.WriteLine(foo(7));
Funkcja anonimowa nie może być przypisana do pośrednio typowanej zmiennej, ponieważ składnia wyrażenia lambda może być używany dla oznaczenia funkcji anonimowej lub drzewa wyrażeń, a wybór nie może być podejmowany automatycznie przez kompilator. Na przykład to nie działa:
// NIE skompiluje sie!
var foo = (int x) => x*x;
Jednakże wyrażenie lambda może brać udział w interferencji typów, np. aby użyć funkcji anonimowej z funkcjonalnością Map dostępną z System.
Collections.Generic.
List
(w metodzie ConvertAll()
):
// Inicjalizowanie listy:
var values = new List<int>() { 7, 13, 4, 9, 3 };
// Mapuj wszystkie elementy listy przez funkcje anonimowa, zwroc nowa liste
var foo = values.ConvertAll(d => d*d) ;
// Rezultat w zmiennej foo jest typu System.Collections.Generic.List<Int32>
Poprzednie wersje C# miały bardziej ograniczoną obsługę funkcji anonimowych. C# w wersji v1.0, wprowadzonej w lutym 2002 roku z NET Framework v1.0, zapewniał częściowa obsługę za pomocą delegatów. Konstrukcja ta jest bardzo podobna do delegatów w PHP. W C# 1.0, delegaci są do podobni do wskaźników na funkcje, które odnoszą się do jednoznacznie nazwanej metody wewnątrz klasy. (Ale w przeciwieństwie do PHP nazwa nie jest potrzebna przy użyciu delegata.) C# w wersji v2.0, wydany w listopadzie 2005 roku razem z .NET Framework v2.0, wprowadził pojęcie metod anonimowych, jako sposób na pisanie wstawianych nienazwanych wyrażeń. C# 3.0 kontynuuje wsparcie dla tych konstrukcji, ale wspiera także funkcje lambda.
Ten przykład będzie się kompilować w C# 3.0 i pokazuje wszystkie trzy metody:
public class TestDriver
{
delegate int SquareDelegate(int d);
static int Square(int d)
{
return d*d;
}
static void Main(string[] args)
{
// C# 1.0: Pierwotna skladnia delegatow wymagala
// inicjalizacji z metoda nazwana.
SquareDelegate A = new SquareDelegate(Square);
System.Console.WriteLine(A(3));
// C# 2.0: Delegat moze byc zainicjalizowany
// z kodem typu inline, zwanym „metoda anonimowa”. Ta
// metoda pobiera int jako parametr.
SquareDelegate B = delegate(int d) { return d*d; };
System.Console.WriteLine(B(5));
// C# 3.0. Delegat moze byc zainicjalizowany poprzez
// wyrazenie lambda. Lambda pobiera int, oraz zwraca int.
// Typ x jest wyprowadzany przez kompilator.
SquareDelegate C = x => x*x;
System.Console.WriteLine(C(7));
// C# 3.0. Delegat akceptujacy jedno wejscie
// zwracajacy jedno wyjscie moze byc posrednio zdefiniowany z typem Func<>.
System.Func<int,int> D = x => x*x;
System.Console.WriteLine(D(9));
}
}
W przypadku C# w wersji 2.0, kompilator C# akceptuje blok kodu funkcji anonimowej i tworzy statyczną funkcję prywatną. Oczywiście, wewnętrznie funkcja ta otrzymuje automatycznie wygenerowaną nazwę, opartą na nazwie metody, w której dokonano deklaracji Delegata. Jednak ta nazwa, z wyjątkiem użycia refleksji, nie jest wystawiona na kod aplikacji.
W przypadku 3.0 C#, stosowany jest ten sam mechanizm.
CFML
[edytuj | edytuj kod]fn = function(){
// instrukcje
};
CFML obsługuje wszelkie instrukcje w definicji funkcji, a nie tylko wyrażenia.
CFML obsługuje rekurencyjne funkcje anonimowe:
factorial = function(n){
return n > 1 ? n * factorial(n-1) : 1;
};
Funkcje anonimowe w CFML implementują domknięcie.
D
[edytuj | edytuj kod]D w celu implementacji funkcji anonimowych wykorzystuje delegatów inline. Pełna składnia dla delegata inline to
zwracany_typ delegate(argumenty){/*cialo*/}
Jeśli jest to jednoznaczne, zwracany typ i słowo kluczowe delegate może być pominięte.
(x){return x*x;}
delegate (x){return x*x;} // jezeli wymagane jest wieksza rozleglosc
(int x){return x*x;} // jezeli parametr nie moze byc wyprowadzony
delegate (int x){return x*x;} // ditto
delegate double(int x){return x*x;} // jezeli typ zwracanej wartosci musi byc wymuszony
Począwszy od wersji 2.0 D, jeżeli kompilator nie dowiedzie, że jest to niepotrzebne, alokuje domknięcia na stercie. Aby wymusić alokacje na stosie, może być użyte słowo kluczowe scope
. Począwszy od wersji 2.058, można użyć skróconej notacji:
x => x*x;
(int x) => x*x;
(x,y) => x*y;
(int x, int y) => x*y;
Anonimową funkcję można przypisać do zmiennej i użyć w ten sposób:
auto sqr = (double x){return x*x;};
double y = sqr(4);
Dart
[edytuj | edytuj kod]Dart wspiera funkcje anonimowe.
var sqr = (x) => x * x;
print(sqr(5));
lub
print(((x) => x * x)(5));
Delphi
[edytuj | edytuj kod]Delphi obsługuje funkcje anonimowe począwszy od wersji 2009.
program demo;
type
TSimpleProcedure = reference to procedure;
TSimpleFunction = reference to function(x: string): Integer;
var
x1: TSimpleProcedure;
y1: TSimpleFunction;
begin
x1 := procedure
begin
Writeln('Hello World');
end;
x1; // wywolanie wlasnie zdefiniowanej metody anonimowej
y1 := function(x: string): Integer
begin
Result := Length(x);
end;
Writeln(y1('bar'));
end.
Erlang
[edytuj | edytuj kod]Eгlang używa podobnej składni dla funkcji anonimowych jak i funkcji nazwanych.
% Funkcja anonimowa przypisana do zmiennej Square
Square = fun(X) -> X * X end.
% Funkcja nazwana o tej samej roli
square(X) -> X * X.
Go
[edytuj | edytuj kod]Go wspiera funkcja anonimowe.
foo := func(x int) int {
return x * x
}
fmt.Println(foo(10))
Haskell
[edytuj | edytuj kod]Haskell wykorzystuje zwięzłą składnię dla funkcji anonimowych (wyrażeń lambda).
\x -> x * x
Wyrażenia lambda są w pełni zintegrowane z silnikiem typowania, i obsługują całą składnie i wszystkie funkcjonalności „zwykłych” funkcji (z wyjątkiem korzystania z wielu definicji dla dopasowywania wzorców, ponieważ lista argumentów jest podawana tylko raz).
map (\x -> x * x) [1..5] -- zwraca [1, 4, 9, 16, 25]
Następujące wyrażenia są sobie równoważne:
f x y = x + y
f x = \y -> x + y
f = \x y -> x + y
Haxe
[edytuj | edytuj kod]W HaXe funkcje anonimowe nazywane są lambdami i używają składni function(lista argumentow) wyrazenie;
.
var f = function(x) return x*x;
f(8); // 64
(function(x,y) return x+y)(5,6); // 11
Java
[edytuj | edytuj kod]Java obsługuje funkcji anonimowe począwszy od wersji JDK 8[10]. W języku java, funkcje anonimowe nazywane są Wyrażeniami Lambda (ang. Lambda Expressions).
Wyrażenie lambda składa się z listy oddzielonych przecinkami parametrów formalnych ujętych w nawiasy, markera strzałki ( -> ) i ciała funkcji. Jeśli istnieje tylko jeden parametr, typy danych parametrów, jak i nawiasy mogą zostać pominięte. Ciało może składać się z jednej instrukcji lub bloku instrukcji[11].
// Bez parametru
() -> System.out.println("Hello, world.")
// Z jednym parametrem (To przykład odzorowania identycznosciowego).
a -> a
// Z prostym wyrazeniem
(a, b) -> a + b
// Z jasna informacja o typie
(long id, String name) -> "id: " + id + ", name:" + name
// Z blokiem kodu
(a, b) -> { return a + b; }
// Z wieloma instukcjami w ciele funkcji. Wymagany jest blok kodu
// Ten przyklad zawiera zagniezdzone wyrazenia lambda (pierwsza jest take domknieciem).
(id, defaultPrice) -> {
Optional product = productList.stream().filter(p -> p.getId() == id).findFirst();
return product.map(p -> p.getPrice()).orElse(defaultPrice);
}
Wyrażenia lambda są konwertowane na „interfejsy funkcjonalne” (definiowane jako interfejsy, które oprócz jednej czy więcej metod domyślnych lub statycznych zawierają tylko jedną metodę abstrakcyjną[11]), jak pokazano w poniższym przykładzie:
public class Calculator {
interface IntegerMath {
int operation(int a, int b);
default IntegerMath swap() {
return (a, b) -> operation(b, a);
}
}
private static int apply(int a, int b, IntegerMath op) {
return op.operation(a, b);
}
public static void main(String... args) {
IntegerMath addition = (a, b) -> a + b;
IntegerMath subtraction = (a, b) -> a - b;
System.out.println("40 + 2 = " + apply(40, 2, addition));
System.out.println("20 - 10 = " + apply(20, 10, subtraction));
System.out.println("10 - 20 = " + apply(20, 10, subtraction.swap()));
}
}
W tym przykładzie zostaje zadeklarowany interfejs funkcjonalny o nazwie IntegerMath
. Wyrażenia lambda, implementujące IntegerMath
są przekazywane do metody apply()
w celu ich wykonana. Domyślne metody, takie jak swap
określają metody, które można wykonać na funkcjach.
JavaScript
[edytuj | edytuj kod]JavaScript/ECMAScript obsługuje funkcje anonimowe.
alert((function(x){
return x*x;
})(10));
W ES6:
alert((x => x*x)(10));
Konstrukcja ta jest często używane w Bookmarkletach. Na przykład, aby zmienić nazwę bieżącego dokumentu (widoczne w tytule okna przeglądarki) na jego adres URL, następny bookmarklet może sprawiać wrażenie działającego prawidłowo.
javascript:document.title=location.href;
Jednak, ponieważ instrukcja przypisania zwraca wartość (sam adres URL), wiele przeglądarek tworzy nową stronę, aby wyświetlić wartość.
Stosując zamiast tego funkcje anonimową, która nie zwraca wartości:
javascript:(function(){document.title=location.href;})();
Instrukcja function w pierwszej (zewnętrznej) parze nawiasów deklaruje anonimową funkcje, która zostaje później wykonana, o czym świadczy składnia z ostatnimi pustymi nawiasami. Jest to prawie równoznaczne z kodem zaprezentowanym poniżej, który tworzy zmienną f
.
javascript:var f = function(){document.title=location.href;}; f();
Należy korzystać z void(), aby uniknąć nowych stron przy wykonywaniu funkcji anonimowych:
javascript:void(function(){return document.title=location.href;}());
lub bez użycia funkcji anonimowej:
javascript:void(document.title=location.href);
JavaScript posiada subtelne reguły semantyczne dotyczące definiowania, wywoływania oraz ewaluowania funkcji anonimowych. Te trudno dostrzegalne niuanse są bezpośrednią konsekwencją ewaluacji wyrażeń. Poniższe konstrukcje nazywane natychmiastowo-wywoływanymi wyrażeniami funkcyjnymi (ang. immediately-invoked function expression) dobrze to ilustrują:
(function(){ ... }())
i
(function(){ ... })()
Skracając zapis „function(){ ... }
” przez f
, formy konstrukcji są odpowiednio wyrażeniem w nawiasach wewnątrz wyrażenia w nawiasach (f())
i wyrażeniem w nawiasach (f)()
wykonanym przez wyrażenie w nawiasach.
Należy zwrócić uwagę na ogólne niejasności składniowe wyrażeń w nawiasach, argumentów funkcji w nawiasach, oraz argumentów formalnych definicji funkcji podanych w nawiasach. W szczególności, JavaScript definiuje operator ,
(przecinek) w kontekście wyrażeń w nawiasach. Nie jest to przypadkiem, że formy składniowe pokrywają się dla wyrażenia oraz argumentów funkcji (ignorując składnie argumentów formalnych funkcji)! Jeśli f
nie jest zdefiniowane w wyrażeniu powyżej, staje się one (())
i ()()
. Pierwszy nie zapewnia podpowiedzi składniowej odnośnie do funkcji wewnątrz ale drugi MUSI ewaluować pierwszy nawias jako funkcje aby być prawidłowym kodem JavaScript. (Oprócz tego: przykładowo ()
może być ([],{},42,"Abc",function () {}), byleby wyrażenie ewaluowało się jako funkcja.)
Ponadto, funkcja jest instancją obiektu (podobnie obiekty są instancjami funkcji), a notacja dla literałów obiektów, {}
dla kodu w klamrach, jest używana przy definiowaniu funkcji w taki sposób (w przeciwieństwie do korzystania z new Function(...)
). W najszerszym nieścisłym sensie (szczególnie z uwzględnieniem naruszenia powiązań globalnych), dowolna sekwencja instrukcji JavaScript w klamrach, {stuff}
, może być traktowana jako punkt stały poniższego wyrażenia
(function(){( function(){( ... {( function(){stuff}() )} ... )}() )}() )
Bardziej poprawnie, ale z zastrzeżeniami
( function(){stuff}() ) ~=
A_Fixed_Point_of(
function(){ return function(){ return ... { return function(){stuff}() } ... }() }()
)
Należy zwrócić uwagę na konsekwencje zastosowania funkcji anonimowych we fragmentach JavaScript, poniżej:
function(){ ... }()
bez otaczających()
, zwykle nie jest prawidłowa(f=function(){ ... })
nie „zapomnina” globalnie of
w przeciwieństwie do(function f(){ ... }
- Wskaźniki wydajności do analizy złożoności przestrzennej i czasowej wywołania funkcji, wywołań na stosie, itp. silnika interpretera JavaScript dają się łatwo zaimplementować za pomocą powyższej konstrukcji funkcji anonimowych. Z interpretacji wyników można pozyskać pewne wiadomości odnośnie do implementacji silnika, dotyczące wydajności iteracji oraz rekursji, a w szczególności rekurencji ogonowej.
Julia
[edytuj | edytuj kod]
W języku Julia funkcje anonimowe są definiowane przy użyciu składni (argumenty)->(wyrażenie)
,
julia> f = x -> x*x; f(8)
64
julia> ((x,y)->x+y)(5,6)
11
Lisp
[edytuj | edytuj kod]Lisp i Scheme obsługują funkcje anonimowe z pomocą konstrukcji „lambda”, które odnoszą się do rachunku lambda. Clojure obsługuje funkcje anonimowe za pomocą konstrukcji „fn” oraz makra #() czytnika.
(lambda (arg) (* arg arg))
Common Lisp
[edytuj | edytuj kod]Common Lisp zawiera koncepcje wyrażeń lambda. Wyrażenie lambda jest zapisywane w postaci listy z symbolem „lambda” jako pierwszy element. Lista zawiera także listę argumentów, dokumentacji i deklaracji, oraz ciało funkcji. Wyrażenia lambda mogą być używane wewnątrz innej lambdy ze specjalnym operatorem „function”.
(function (lambda (arg) (do-something arg)))
„function” może być zapisane w skrócie jako #'. Ponadto istnieje makro „lambda”, które rozwija się do postaci funkcji:
; Uzywajac ostrego cudzyslowia
# '(lambda (arg) (do-something arg))
; Uzywajac makra lambda:
(lambda (arg) (do-something arg))
Jednym z typowych zastosowań funkcji anonimowych w Common Lisp jest przekazywanie ich funkcjom wyższego rzędu jak „mapcar”. „mapcar” wywołuje funkcje dla każdego elementu listy i zwraca listę jako rezultat.
(mapcar #'(lambda (x) (* x x))
'(1 2 3 4))
; -> (1 4 9 16)
„lambda formy” w Common Lisp pozwalają „wyrażeniom lambda” na bycie używanymi w wywołaniach funkcji:
((lambda (x y)
(+ (sqrt x) (sqrt y)))
10.0
12.0)
Funkcje anonimowe w Common Lisp mogą, po uprzedniej definicji, mieć nadawane nazwy:
(setf (symbol-function 'sqr)
(lambda (x) (* x x)))
; co pozwala na jej wywolanie przy uzyciu identyfikatora SQR:
(sqr 10.0)
Scheme
[edytuj | edytuj kod]Co ciekawe, „funkcje nazwane” - to po prostu cukier syntaktyczny dla funkcji anonimowych powiązanych z nazwami:
(define (somename arg)
(do-something arg))
rozwija się do (jest równoważne z)
(define somename
(lambda (arg)
(do-something arg)))
Clojure
[edytuj | edytuj kod]Clojure obsługuje funkcje anonimowe za pomocą konstrukcji „fn”:
(fn [x] (+ x 3))
Istnieje również składnia czytnika służąca do definicji wyrażenia lambda:
# (+ % %2%3) ; Definiuje funkcje anonimowa, która pobiera trzy argumenty i je sumuje.
Jak Scheme, „funkcje nazwane” Clojure to po prostu cukier syntaktyczny dla wyrażeń lambda związanych z nazwami:
(defn func [arg] (+ 3 arg))
rozwija się do:
(def func (fn [arg] (+ 3 arg)))
Lua
[edytuj | edytuj kod]W Lua (jak w Scheme) wszystkie funkcje są anonimowe. Funkcja nazwana w lua-to po prostu zmienna, która przechowuje referencje do obiektu funkcyjnego[12].
Tak więc, w lua
function foo(x) return 2*x end
to po prostu cukier syntaktyczny dla
foo = function(x) return 2*x end
Przykład użycia funkcji anonimowych do sortowania w odwrotnej kolejności:
table.sort(network, function(a,b)
return a.name > b.name
end)
Wolfram Language/Mathematica
[edytuj | edytuj kod]Wolfram Language to język programowania systemu Mathematica. Funkcje anonimowe są ważnym elementem programowania w systemie Mathematica. Istnieje kilka sposobów ich tworzenia. Poniżej znajduje się kilka funkcji anonimowych, które inkrementują liczbę. Pierwszy sposób jest najbardziej powszechny. '#1' odnosi się do pierwszego argumentu a '&' oznacza koniec funkcji anonimowej.
#1+1&
Function[x,x+1]
x \[Function] x+1
Tak więc, na przykład:
f:= #1^2&;f[8]
64
#1+#2&[5,6]
11
Ponadto, Mathematica ma dodatkową konstrukcję do budowy rekursywnych anonimowych funkcji. Symbol '#0' odnosi się do całej funkcji. Poniższa funkcja oblicza silnię swojego wejścia:
If[#1 == 1, 1, #1 * #0[#1-1]]&
MATLAB/Octave
[edytuj | edytuj kod]
Funkcje anonimowe w GNU Octave lub w MATLAB są definiowane za pomocą składni @(lista-argumentów)wyrażenie
. Wszystkie zmienne, które nie znalazły się na liście argumentów są dziedziczone z zawierającego zasięgu.
> f = @(x)x*x; f(8)
ans = 64
> (@(x,y)x+y)(5,6) % Dziala tylko w Octave
ans = 11
Maxima
[edytuj | edytuj kod]
W języku Maxima funkcje anonimowe są definiowane przy użyciu składni lambda(list argumentów,wyrażenie)
,
f: lambda([x],x*x); f(8); 64 lambda([x,y],x+y)(5,6); 11
ML
[edytuj | edytuj kod]Przeróżne dialekty ML wspierają funkcje anonimowe.
fun arg -> arg * arg
F#
[edytuj | edytuj kod](fun x -> x * x) 20 // 400
fn arg => arg * arg
Perl
[edytuj | edytuj kod]Perl 5
[edytuj | edytuj kod]Perl 5 wpiera funkcje anonimowe, w następujący sposób:
(sub { print "I got called\n" })->(); # 1. calkowicie anonimowa, wywolywana przy utworzeniu
my $squarer = sub { my $x = shift; $x * $x }; # 2. przypisana do zmiennej
sub curry {
my ($sub, @args) = @_;
return sub { $sub->(@args, @_) }; # 3. jako wartosc zwracana przez inna funkcje
}
# przyklad Curryingu w Perl'u
sub sum { my $tot = 0; $tot += $_ for @_; $tot } # zwraca sume argumentow
my $curried = curry \&sum, 5, 7, 9;
print $curried->(1,2,3), "\n"; # wypisuje 27 ( = 5 + 7 + 9 + 1 + 2 + 3 )
Inne konstrukcje pobierają „gołe bloki” (ang. bare blocks) jako argumenty, co pełni rolę podobną do funkcji lambda z jednym parametrem, w ten sposób nie utrzymując tej samej konwencji przekazywania parametrów, gdyż @_ nie jest ustawione.
my @squares = map { $_ * $_ } 1..10; # map i grep nie uzywaja slowa kluczowego 'sub'
my @square2 = map $_ * $_, 1..10; # nawiasy nie potrzebne dla pojedynczego wyrazenia
my @bad_example = map { print for @_ } 1..10; # przekazywanie wartosci jak normalna funkcja Perl'a
Perl 6
[edytuj | edytuj kod]W języku Perl 6, wszystkie bloki (nawet te, które są związane z if, while itp.) to funkcje anonimowe. Blok, który nie jest używany jako R-wartość, jest wykonywany natychmiast.
# 1. calkowicie anonimowa, wywolywana przy utworzeniu
{ say "I got called" };
# 2. przypisana do wartosci
my $squarer1 = -> $x { $x * $x }; # 2a. "pointy block"
my $squarer2 = { $^x * $^x }; # 2b. "twigil"
my $squarer3 = { my $x = shift @_; $x * $x }; # 2b. styl Perl 5
# 3. currying
sub add ($m, $n) { $m + $n }
my $seven = add(3, 4);
my $add_one = &add.assuming(m => 1);
my $eight = $add_one($seven);
PHP
[edytuj | edytuj kod]Przed wydaniem wersji 4.0.1, PHP nie wspierał funkcji anonimowych[13].
PHP w od 4.0.1 do 5,3
[edytuj | edytuj kod]
PHP 4.0.1 wprowadził create_function
, co było zaczątkiem wsparcia funkcji anonimowych. Wywołanie tej funkcji tworzy nową funkcję z losowo wygenerowaną nazwą (w postaci łańcucha)
$foo = create_function('$x', 'return $x*$x;');
$bar = create_function("\$x", "return \$x*\$x;");
echo $foo(10);
Ważne jest, aby pamiętać, że lista argumentów i ciało funkcji powinny być w pojedynczych apostrofach lub znaki dolara muszą być oznaczone znakami modyfikacji.
W przeciwnym razie PHP będzie uważać „$x
” za zmienną $x
i podstawi ją w łańcuch znaków (pomimo możliwości braku jej definicji), zamiast pozostawić w „$x
” w łańcuchu.
Dla funkcji z cytatami lub funkcji z dużą ilością zmiennych zapewnianie prawidłowej interpretacji może stać się całkiem uciążliwe.
Należy również zaznaczyć, że każde wywołanie create_function
stworzy nową funkcję, która będzie istnieć aż do końca programu, i nie może być usunięta przy pomocu „garbage collectora”. Jeśli ktoś używa tej techniki ponad miarę (np. w pętli), może spowodować nieodwracalne zajęcie pamięci programu.
PHP 5.3
[edytuj | edytuj kod]
W PHP 5.3 dodano nową klasę, zwaną Closure
i magiczną metodę __invoke()
, która sprawia, że instancja klasy jest wywoływana[14].
$x = 3;
$func = function($z) { return $z *= 2; };
echo $func($x); // wypisuje 6
W tym przykładzie, $func
jest instancją Closure
, a echo $func()
jest odpowiednikiem $func->__invoke($z)
. PHP 5.3 naśladuje funkcje anonimowe, ale on nie obsługuje prawdziwych funkcji anonimowych, ponieważ funkcje w PHP wciąż nie są obiektami pierwszego rzędu.
Z PHP 5.3 obsługuje domknięcia, ale zmienne muszą być wyraźnie oznaczane jako takowe:
$x = 3;
$func = function() use(&$x) { $x *= 2; };
$func();
echo $x; // wypisuje 6
Zmienna $x
jest powiązana za pomocą referencji, a więc wywołanie $func
modyfikuje ją tak, że zmiany są widoczne poza samą funkcją.
Dialekty języka Prolog
[edytuj | edytuj kod]Logtalk
[edytuj | edytuj kod]Logtalk używa następującej składni dla predykatów anonimowych (wyrażeń lambda):
{FreeVar1, FreeVar2, ...}/[LambdaParameter1, LambdaParameter2, ...]>>Goal
Prosty przykład bez wolnych zmiennych i z użyciem predykatu mapującego listy:
| ?- meta::map([X,Y]>>(Y is 2*X), [1,2,3], Ys).
Ys = [2,4,6]
yes
Currying również jest wspierany. Powyższy przykład może być zapisany jako:
| ?- meta::map([X]>>([Y]>>(Y is 2*X)), [1,2,3], Ys).
Ys = [2,4,6]
yes
Visual Prolog
[edytuj | edytuj kod]Funkcje anonimowe (generalnie rzecz biorąc anonimowe predykaty) zostały wprowadzone w ramach Prologu 7.2.[15] Anonimowe predykaty może pobierać wartości z kontekstu. Jeśli obiekt został stworzony w składowej obiektu może również uzyskać dostęp do stanu obiektu (poprzez pobieranie This
).
mkAdder
zwraca anonimową funkcję, która pobrała argument x
z domknięcia. Zwracana funkcja to funkcja, która dodaje x
do jej argumentu:
clauses
mkAdder(X) = { (Y) = X+Y }.
Python
[edytuj | edytuj kod]Python obsługuje proste funkcje anonimowe w postaci form Lambda. Wykonywalne ciało lambdy musi być wyrażeniem i nie może być pełną instrukcją, które jest ograniczeniem, która ogranicza jego użyteczność. Wartość zwracana za pomocą wyrażenia lambda jest wartością zawartego w niej wyrażenia. Formy lambda mogą być używane wszędzie tam, gdzie zwykłe funkcje, jednak ograniczenia te sprawiają, że będą to bardzo ograniczona wersja normalnej funkcji. Oto przykład:
>>> foo = lambda x: x*x
>>> print(foo(10))
100
W zasadzie, konwencja Pythona, podobnie jak ma to miejsce w innych językach, zachęca do korzystania z nazwanych funkcji zdefiniowanych w tym samym zasięgu. Jest to dopuszczalne, gdyż funkcje zdefiniowane lokalnie implementują pełnie funkcjonalności domknięć, a Pythonie są prawie tak samo wydajne jak lambdy. Można powiedzieć, że w poniższy przykładzie wbudowana funkcja potęgowania poddana została technice curry:
>>> def make_pow(n):
... def fixed_exponent_pow(x):
... return pow(x, n)
... return fixed_exponent_pow
...
>>> sqr = make_pow(2)
>>> print (sqr(10))
100
>>> cub = make_pow(3)
>>> print (cub(10))
1000
R
[edytuj | edytuj kod]
W GNU R funkcje anonimowe są definiowane z użyciem składni function(argument-list)expression
.
> f <- function(x)x*x; f(8)
[1] 64
> (function(x,y)x+y)(5,6)
[1] 11
Ruby
[edytuj | edytuj kod]
Rubin obsługuje funkcje anonimowe za pomocą struktury składniowej, zwanej blokiem. Istnieją dwa typy danych dla bloków. Proc
zachowują się podobnie do domknięć, a lambda
zachowują się bardziej jak funkcje anonimowa[16]. Blok przekazywany do metody może w pewnych sytuacjach być konwertowany do postaci Proc.
irb(main):001:0> # Przyklad 1:
irb(main):002:0* # Funkcja w pelni anonimowa z uzyciem bloku.
irb(main):003:0* ex = [16.2, 24.1, 48.3, 32.4, 8.5]
=> [16.2, 24.1, 48.3, 32.4, 8.5]
irb(main):004:0> ex.sort_by { |x| x - x.to_i } # Sortuj po czesci ulamkowej, ignoruj czesc calkowita.
=> [24.1, 16.2, 48.3, 32.4, 8.5]
irb(main):005:0> # Przyklad 2:
irb(main):006:0* # Funkcje pierwszego rzedu jako jednoznaczny obiekt klasy proc
irb(main):007:0* ex = Proc.new { puts "Hello, world!" }
=> #
irb(main):008:0> ex.call
Hello, world!
=> nil
irb(main):009:0> # Przyklad 3:
irb(main):010:0* # Funkcja, ktora zwraca funkcje lambda z parametrami
irb(main):011:0* def is_multiple_of(n)
irb(main):012:1> lambda{|x| x % n == 0}
irb(main):013:1> end
=> nil
irb(main):014:0> multiple_four = is_multiple_of(4)
=> #
irb(main):015:0> multiple_four.call(16)
=> true
irb(main):016:0> multiple_four[15]
=> false
Scala
[edytuj | edytuj kod]W Scala, funkcje anonimowe wykorzystują następującą składnię:[17]
(x: Int, y: Int) => x + y
W pewnych kontekstach, na przykład gdy funkcja anonimowa jest parametrem przekazywanym do innej funkcji, kompilator może przewidzieć typy parametrów funkcji anonimowej, i mogą być one pominięte w składni. W takich sytuacjach można również użyć wersji skrótowej dla funkcji anonimowych z użyciem znaku podkreślenia, wprowadzając w ten sposób nienazwane parametry.
val list = List(1, 2, 3, 4)
list.reduceLeft( (x, y) => x + y )
// Tutaj kompilator potrafi dojsc do typow x i y : Int, Int
// Tak wiec, nie wymagane sa adnotacje parametrow funkcji anonimowych
list.reduceLeft( _ + _ )
// Podkreslenie oznacza nowy, nienazwany parametr funkcji anonimowej
// To prowadzi do nawet prostszego ekwiwalentu funkcji anonimowej powyzej
Smalltalk
[edytuj | edytuj kod]W języku Smalltalk anonimowe funkcje nazywane są blokami
[ :x | x*x ] value: 4
„zwraca 16”
Swift
[edytuj | edytuj kod]W Swift, funkcje anonimowe nazywane są domknięciami[18]. Składnia ma następującą formę:
{ (parameters) -> returnType in
statement
}
Na przykład:
{ (s1: String, s2: String) -> Bool in
return s1 > s2
}
Dla zwięzłości i ekspresywności, jeśli typy parametrów oraz typ wartości zwracanej da się wywnioskować w czasie kompilacji to mogą one być pominięte:
{ s1, s2 in return s1 > s2 }
Podobnie, Swift obsługuje również niejawne instrukcje return dla domknięć z jedną instrukcją:
{ s1, s2 in s1 > s2 }
Wreszcie, nazwy parametrów mogą być również pominięte. W takim przypadku można się do nich odnosić za pomocą tzw. skrótowych nazw argumentów, składających się z symbolu $, oraz ich pozycji (na przykład, $0, $1, $2 itd.):
{ $0 > $1 }
TCL
[edytuj | edytuj kod]W Tcl, zastosowanie funkcji anonimowej do podniesienia liczby 2 do kwadratu wygląda następująco:[19]
apply {x {expr {$x*$x}}} 2
# zwraca 4
Należy zauważyć, że w tym przykładzie biorą udział dwie „potencjalne funkcje” języka TCL. Najbardziej uniwersalny jest sposób o nazwie przedrostka polecenia (ang. command prefix), i jeśli zmienna f przechowuje taką funkcję, wtedy wywołanie funkcji f(x) będzie wyglądało następująco:
{*}$f $x
gdzie {*}
- to prefiks rozszerzenia (nowość w TCL 8.5). Prefiks polecenia w powyższym przykładzie to apply {x {x {expr {$x*$x}}}
. Nazwy poleceń mogą być powiązane z prefiksami z użyciem polecenia interp alias
. Prefiksy poleceń obsługują currying. Prefiksy poleceń są bardzo powszechne w API TCL.
Inny kandydat na „funkcję” w TCL bywa zwykle nazywany „lambda”, i jest obecny w powyższym przykładzie jako {x {expr {$x*$x}}}
. To ta część pobiera skompilowaną postać funkcji anonimowej, jednak może ona być wywołana tylko za pomocą przekazania polecenia apply
. Wyrażenia lambda nie wspierają currying'u, chyba że w połączeniu z apply
, aby utworzyć prefiks wyrażenia. Lambda rzadko bywają częścią API TCL.
Visual Basic .NET
[edytuj | edytuj kod]Visual Basic.NET 2008 wprowadził funkcje anonimowe w postaci wyrażeń lambda. W połączeniu z niejednoznacznym modelem kodowania, VB zapewnia ekonomiczną składnie funkcji anonimowych. Jak w Pythonie, w VB.NET, funkcje anonimowe powinny być definiowane w jednej linii; nie mogą one być złożonymi instrukcjami. Ponadto funkcje anonimowe w VB.NET muszą być typu „Function
” - muszą zwracać wartość.
Dim foo = Function(x) x * x
Console.WriteLine(foo(10))
Visual Basic.NET 2010 dodał wsparcie dla wielowierszowych wyrażeń lambda i funkcji anonimowych, bez wartości zwracanej. Poniżej przykład funkcji do wykorzystania w wątku (Thread).
Dim t As New System.Threading.Thread(Sub()
For n as Integer = 0 to 10 'Licz do 10
Console.WriteLine(n) 'Wypisuj kazda liczbe
Next
End Sub)
t.Start()
Zobacz też
[edytuj | edytuj kod]Przypisy
[edytuj | edytuj kod]- ↑ "Higher order functions". learnyouahaskell.com
- ↑ Fernandez, Maribel (2009), Models of Computation: An Introduction to Computability Theory, Undergraduate Topics in Computer Science, Springer Science & Business Media, p. 33, ISBN 978-1-84882-434-8,
The Lambda calculus ... was introduced by Alonzo Church in the 1930s as a precise notation for a theory of anonymous functions
- ↑ "Lecture 29: Fixpoints and Recursion".
- ↑ "Bash lambda".
- ↑ "Closure support". getrailo.org. [zarchiwizowane z tego adresu (2014-01-06)]..
- ↑ "Whats new in ColdFusion 10". [dostęp 2016-02-10]. [zarchiwizowane z tego adresu (2014-01-06)].
- ↑ "Managed COBOL Reference".
- ↑ "Quotations - Factor Documentation"
- ↑ Järvi, Jaakko; Powell, Gary (n.d.
- ↑ What's New in JDK 8 [online], www.oracle.com [dostęp 2017-11-18] .
- ↑ a b The Java Tutorials: Lambda Expressions, docs.oracle.com
- ↑ "Programming in Lua - More about Functions".
- ↑ http://php.net/create_function the top of the page indicates this with "(PHP 4 >= 4.0.1, PHP 5)"
- ↑ PHP: rfc:closures [online], wiki.php.net [dostęp 2019-04-07] [zarchiwizowane z adresu 2012-12-20] .
- ↑ "Anonymous Predicates". in Visual Prolog Language Reference
- ↑ Sosinski, Robert (2008-12-21).
- ↑ Zarchiwizowana kopia. [dostęp 2016-02-10]. [zarchiwizowane z tego adresu (2013-07-23)].
- ↑ The Swift Programming Language (Swift 4): Closures [online], developer.apple.com [dostęp 2017-11-18] (ang.).
- ↑ apply manual page, retrieved 2012-09-06.
Linki zewnętrzne
[edytuj | edytuj kod]- Metody Anonimowe - Kiedy Powinny One Być Stosowane? (blog o anonimowych funkcjach w Delphi)
- W C# Wyrażenia Lambda
- Sporządzanie Lambda-wyrażeń: Scala vs Java 8
- anonimowe funkcje w PHP