В комп'ютерному програмуванні, анонімною функцією (інші назви функціональний літерал або лямбда-вираз, лямбда-функція) називається така функція, яка визначена без вказання пов'язаного з нею ідентифікатора. Анонімні функція зазвичай: передаються у вигляді аргументу у функції вищого порядку, або використовуються для побудови результату функції вищого порядку, яка має повертати функцію.
Якщо функція використовується в програмі лише раз, або обмежену визначену кількість раз, використання анонімної функції може бути більш синтаксично простішим ніж використання іменованої функції. Анонімні функції широко застосовуються у функціональних мовах програмування і інших мовах програмування з функціями першого класу, де вони виконують ту саму роль для функціонального типу, що і літерали для інших типів даних.
Анонімні функції були представлені в роботі Алонзо Черча як винайдене ним лямбда числення у 1936 (до появи електронно-обчислюваних машин), в якому всі функції анонімні. В деяких мовах програмування, анонімні функції оголошуються з використанням ключового слова lambda, а самі анонімні функції часто називаються лямбда абстракціями або лямбда виразами. Анонімні функції почали використовуватись в мовах програмування починаючи з мови Lisp в 1958 і тепер дедалі більше сучасних мов програмування підтримують анонімні функції.
Анонімні функції є формою вкладених функцій, в тому плані, що вони дозволяють доступ до змінних в зоні видимості функції, в яку вони вкладені (не локальні змінні). Це означає, що анонімні функції повинні визначатися з використанням замикань. На відміну від іменованих вкладених функцій, вони не можуть бути рекурсивними без використання оператору фіксованої точки (також називається анонімною фіксованою точкою або анонімною рекурсією).
Приклади Багато мов програмування мають підтримку анонімних функцій, або чогось подібного.
C (не стандартне розширення) Анонімні функції не підтримуються стандартним C, але підтримуються деякими його діалектами, такими як gcc і clang.
GCC GCC має підтримку анонімних функцій, що є поєднанням вкладених функцій і операторних виразів. Вони мають таку форму: ( { return_type anonymous_functions_name (parameters) { function_body } anonymous_functions_name; } )
Даний приклад працює лише в проєктах, що компілюються GCC. Слід відмітити, що завдяки роботі макроса, якщо ваша 'l_body' матиме коми за межами дужок, тоді цей код не скомпілюється оскільки gcc використовує кому як роздільник для наступного аргументу макросу. Аргумент 'l_ret_type' можна прибрати, якщо доступне використання '__typeof__'; в наведеному прикладі використання __typeof__ для масиву поверне 'testtype *', який можна розіменувати для отримання фактичного значення, якщо це необхідно. #include <stdio.h>
//* це є визначення анонімної функції */ #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); /* анонімна функція задається як функція для 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; }
Java Java, починаючи з JDK 8, підтримує анонімні функції під назвою лямбда-вирази ().
Лямбда-вираз складається з розділеного комами списку формальних параметрів, укладених у дужки, символу стрілки (->) та тіла. Типи даних параметрів завжди можна опустити, як і дужки, якщо є тільки один параметр. Тіло може складатися з однієї інструкції або блоку інструкцій.
// без параметрів () -> System.out.println("Hello, world.")
// з одним параметром (цей приклад є функцією тотожності). a -> a
// з одним виразом (a, b) -> a + b
// з явним зазначенням типів (long id, String name) -> String.format("id: %f, name: %s", id, name)
// з блоком коду (a, b) -> { return a + b; }
// з декількома інструкціями в тілі лямбда. Потрібен блок коду. // Цей приклад також містить два вкладені лямбда-вирази (перший з них також є замиканням). (id, defaultPrice) -> { Optional<Product> product = productList.stream().filter(p -> p.getId() == id).findFirst(); return product.map(p -> p.getPrice()).orElse(defaultPrice); }
Лямбда-вирази перетворюються на «функційні інтерфейси» (визначені як інтерфейси, що містять лише один абстрактний метод на додаток до одного або декількох типових методів або статичних методів),The Java Tutorials: Lambda Expressions, docs.oracle.com як у такому прикладі:
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.printf("40 + 2 = %d%n", apply(40, 2, addition)); System.out.printf("20 - 10 = %d%n", apply(20, 10, subtraction)); System.out.printf("10 - 20 = %d%n", apply(20, 10, subtraction.swap()));
У цьому прикладі оголошено функційний інтерфейс під назвою IntegerMath. Лямбда-вирази, які реалізують IntegerMath передаються в метод apply() для виконання. Типові методи, як swap, визначають методи у функції.
У Java 8 для створення лямбди на наявному методі введено інший механізм під назвою посилання на метод (оператор ::). Посилання на метод не вказує кількості або типів аргументів, оскільки вони дістаються з абстрактного методу функціонального інтерфейсу. IntBinaryOperator sum = Integer::sum; У наведеному вище прикладі функційний інтерфейс IntBinaryOperator оголошує абстрактний метод int applyAsInt(int, int), тож компілятор шукає метод int sum(int, int) у класі java.lang.Integer.
Відмінності від анонімних класів
Анонімні класи інтерфейсів, сумісних з лямбда-виразами, схожі, але не повністю еквівалентні лямбда-виразам. Для ілюстрації, у цьому прикладі і є примірниками які додають свої два параметри:
IntegerMath anonymousClass = new IntegerMath() { @Override public int operation(int a, int b) { return a + b;
IntegerMath lambdaExpression = (a, b) -> a + b;
Основна відмінність полягає в тому, що лямбда-вираз не обов'язково повинен виділяти новий примірник для , і щоразу, коли виконується цей код, може повертати той самий примірник. Додатково, принаймні в OpenJDK, лямбди компілюються в інструкції , з тілом лямбди, вставленим як статичний метод в охоплювальний клас, замість того, щоб створювати повністю новий файл класу.
Обмеження Java Лямбда-вирази в Java 8 мають такі обмеження: Вони можуть генерувати перевірені винятки, але такі лямбда-вирази не працюватимуть з інтерфейсами, використовуваними колекціями API. До змінних, що містяться в області дії, де оголошено лямбда-вираз, можна отримати доступ із лямбда-виразу, тільки якщо вони є фактично остаточними, тобто якщо змінна не мутується всередині або поза областю дії лямбда-виразу.
Див. також Лямбда-вирази у С++
Примітки