В компьютерном программировании анонимная функция ( функциональный литерал , лямбда-абстракция , лямбда-функция , лямбда-выражение или блок ) — это определение функции , не привязанное к идентификатору . Анонимные функции часто представляют собой аргументы, передаваемые функциям более высокого порядка или используемые для создания результата функции более высокого порядка, которая должна возвращать функцию. [1] Если функция используется только один раз или ограниченное количество раз, анонимная функция может быть синтаксически проще, чем использование именованной функции. Анонимные функции повсеместно распространены в языках функционального программирования и других языках с первоклассными функциями , где они выполняют для типа функции ту же роль, что и литералы для других типов данных .
Анонимные функции берут свое начало в работе Алонзо Чёрча , когда он изобрел лямбда -исчисление , в котором все функции анонимны, в 1936 году, до появления электронных компьютеров. [2] В некоторых языках программирования анонимные функции вводятся с использованием ключевого слова лямбда , а анонимные функции часто называют лямбда-выражениями или лямбда-абстракциями. Анонимные функции были особенностью языков программирования со времен Лиспа в 1958 году, и все большее число современных языков программирования поддерживают анонимные функции.
Названия «лямбда-абстракция», «лямбда-функция» и «лямбда-выражение» относятся к обозначениям абстракции функции в лямбда-исчислении, где обычная функция f ( x ) = M будет записана (λ x . M ) ( M is выражение, которое использует x ). Сравните с синтаксисом Python .lambda x: M
Название «стрелочная функция» относится к математическому « сопоставлению » символа x ↦ M . Сравните с синтаксисом JavaScript . [3]x => M
Анонимные функции могут использоваться для хранения функций, которым не нужно давать имя, и, возможно, для краткосрочного использования. Некоторые известные примеры включают замыкания и каррирование .
Использование анонимных функций — это вопрос стиля. Их использование никогда не является единственным способом решения проблемы; вместо этого каждая анонимная функция может быть определена как именованная функция и вызываться по имени. Некоторые программисты используют анонимные функции для инкапсуляции конкретного, непереиспользуемого кода, не засоряя его множеством маленьких однострочных обычных функций.
В некоторых языках программирования анонимные функции обычно реализуются для очень конкретных целей, таких как привязка событий к обратным вызовам или создание экземпляра функции для определенных значений, что может быть более эффективным, более читаемым и менее подверженным ошибкам, чем вызов более общей именованной функции. .
Следующие примеры написаны на Python 3.
При попытке сортировки нестандартным способом может быть проще сохранить логику сортировки в виде анонимной функции вместо создания именованной функции. Большинство языков предоставляют общую функцию сортировки, реализующую алгоритм сортировки произвольных объектов. Эта функция обычно принимает произвольную функцию, которая определяет, как сравнивать, равны ли два элемента или один из них больше или меньше другого.
Рассмотрим этот код Python, сортирующий список строк по длине строки:
>>> a = [ 'дом' , 'машина' , 'велосипед' ] >>> a . sort ( key = лямбда x : len ( x )) >>> a [ 'машина' , 'велосипед' , 'дом' ]
Анонимной функцией в этом примере является лямбда-выражение:
лямбда х : лен ( х )
Анонимная функция принимает один аргумент x
и возвращает длину этого аргумента, которая затем используется методом sort()
в качестве критерия сортировки.
Основной синтаксис лямбда-функции в Python:
лямбда arg1 , arg2 , arg3 , ... : < операция над аргументами , возвращающими значение > _
Выражение, возвращаемое лямбда-функцией, можно присвоить переменной и использовать в коде в нескольких местах.
>>> добавить = лямбда а : а + а >>> добавить ( 20 ) 40
Другим примером может быть сортировка элементов в списке по имени их класса (в Python все имеет класс):
>>> а = [ 10 , 'число' , 11,2 ] >>> а . сортировка ( ключ = лямбда x : x . __class__ . __name__ ) >>> a [ 11.2 , 10 , 'число' ]
Обратите внимание, что 11.2
имеет имя класса " float
", 10
имеет имя класса " int
" и 'number'
имеет имя класса " str
". Порядок сортировки: " float
", " int
", затем " str
".
Замыкания — это функции, вычисляемые в среде, содержащей связанные переменные . В следующем примере переменная «порог» привязывается к анонимной функции, которая сравнивает входные данные с порогом.
def comp ( порог ): вернуть лямбда x : x < порог
Это можно использовать как своего рода генератор функций сравнения:
>>> func_a = комп ( 10 ) >>> func_b = комп ( 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
Было бы непрактично создавать функцию для каждой возможной функции сравнения, и может быть слишком неудобно сохранять пороговое значение для дальнейшего использования. Независимо от причины использования замыкания, анонимная функция — это сущность, содержащая функциональность, выполняющую сравнение.
Каррирование — это процесс изменения функции, при котором вместо нескольких входных данных она принимает один входной сигнал и возвращает функцию, которая принимает второй входной сигнал, и так далее. В этом примере функция, выполняющая деление на любое целое число, преобразуется в функцию, выполняющую деление на заданное целое число.
>>> def разделить ( x , y ): ... вернуть x / y>>> def делитель ( d ): ... вернуть лямбда x : разделить ( x , d )>>> половина = делитель ( 2 ) >>> треть = делитель ( 3 )>>> распечатать ( половина ( 32 ), третья ( 32 )) 16,0 10,666666666666666>>> распечатать ( половина ( 40 ), третья ( 40 )) 20.0 13.333333333333334
Хотя использование анонимных функций, возможно, не является обычным явлением при каррировании, его все же можно использовать. В приведенном выше примере функция divisor генерирует функции с указанным делителем. Функции half и Third каррируют функцию деления с фиксированным делителем.
Функция делителя также формирует замыкание, связывая переменную d
.
Функция высшего порядка — это функция, которая принимает функцию в качестве аргумента или возвращает ее в качестве результата. Обычно это используется для настройки поведения обобщенно определенной функции, часто это конструкция цикла или схема рекурсии. Анонимные функции — удобный способ указать аргументы таких функций. Следующие примеры относятся к Python 3.
Функция карты выполняет вызов функции для каждого элемента списка. В следующем примере каждый элемент массива возводится в квадрат с помощью анонимной функции.
>>> a = [ 1 , 2 , 3 , 4 , 5 , 6 ] >>> список ( map ( лямбда x : x * x , a )) [ 1 , 4 , 9 , 16 , 25 , 36 ]
Анонимная функция принимает аргумент и умножает его на себя (возводит в квадрат). Вышеупомянутая форма не одобряется создателями языка, которые утверждают, что форма, представленная ниже, имеет то же значение и больше соответствует философии языка:
>>> a = [ 1 , 2 , 3 , 4 , 5 , 6 ] >>> [ x * x для x в a ] [ 1 , 4 , 9 , 16 , 25 , 36 ]
Функция фильтра возвращает все элементы из списка, которые оценивают True при передаче определенной функции.
>>> a = [ 1 , 2 , 3 , 4 , 5 , 6 ] >>> список ( фильтр ( лямбда x : x % 2 == 0 , a )) [ 2 , 4 , 6 ]
Анонимная функция проверяет, является ли переданный ей аргумент четным. Как и в случае с картой, более подходящей считается форма ниже:
>>> a = [ 1 , 2 , 3 , 4 , 5 , 6 ] >>> [ x для x в a , если x % 2 == 0 ] [ 2 , 4 , 6 ]
Функция сгиба проходит по всем элементам структуры (для списков обычно слева направо, «левая складка», называемая reduce
в Python), накапливая значение по мере его выполнения. Это можно использовать для объединения всех элементов структуры в одно значение, например:
>>> из functools import уменьшить >>> a = [ 1 , 2 , 3 , 4 , 5 ] >>> уменьшить ( лямбда x , y : x * y , a ) 120
Это выполняет
Анонимная функция здесь — это умножение двух аргументов.
Результат свертки не обязательно должен быть одним значением. Вместо этого и карту, и фильтр можно создать с помощью сгиба. В карте накапливаемое значение представляет собой новый список, содержащий результаты применения функции к каждому элементу исходного списка. В фильтре накапливаемое значение представляет собой новый список, содержащий только те элементы, которые соответствуют заданному условию.
Ниже приводится список языков программирования , которые поддерживают безымянные анонимные функции полностью или частично в каком-либо варианте или не поддерживают вообще.
В этой таблице показаны некоторые общие тенденции. Во-первых, все языки, которые не поддерживают анонимные функции ( C , Pascal , Object Pascal ), являются статически типизированными языками. Однако статически типизированные языки могут поддерживать анонимные функции. Например, языки ML статически типизированы и в основном включают анонимные функции, а Delphi , диалект Object Pascal , был расширен для поддержки анонимных функций, как и C++ (по стандарту C++11 ). Во-вторых, языки, которые рассматривают функции как функции первого класса ( Dylan , Haskell , JavaScript , Lisp , ML , Perl , Python , Ruby , Scheme ), обычно имеют поддержку анонимных функций, поэтому функции можно определять и передавать так же легко, как и другие данные. типы.
Многие языки поддерживают анонимные функции или что-то подобное.
Лишь некоторые диалекты поддерживают анонимные функции, например dfns , в неявном стиле или их комбинацию.
f ← { ⍵ × ⍵ } Как dfn f 1 2 3 1 4 9 g ← ⊢ × ⊢ Как неявный 3 - поезд ( вилка ) g 1 2 3 1 4 9 h ← × ⍨ Как производная неявная функция h 1 2 _ 3 1 4 9
Анонимная функция не поддерживается стандартным языком программирования C, но поддерживается некоторыми диалектами C, такими как GCC [53] и Clang .
Коллекция компиляторов GNU (GCC) поддерживает анонимные функции, смешанные с вложенными функциями и выражениями операторов. Он имеет форму:
( { return_type имя_анонимной_функции ( параметры ) { тело_функции } имя анонимной_функции ; } )
Следующий пример работает только с GCC. Из-за того, как макросы раскрываются, они l_body
не могут содержать запятые вне круглых скобок; GCC рассматривает запятую как разделитель между аргументами макроса. Аргумент l_ret_type
можно удалить, если __typeof__
он доступен; в приведенном ниже примере использование __typeof__
on array вернет testtype *
, которое при необходимости можно разыменовать для получения фактического значения.
#include <stdio.h> //* это определение анонимной функции */ #define лямбда(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 { int a ; интервал б ; } Тип теста ; недействительная распечатка ( const testtype * array ) { int i ; for ( i = 0 ; i < 3 ; ++ i ) printf ( «%d %d \n » , массив [ i ] .a , массив [ i ].b ) ; printf ( " \n " ); } int main ( void ) { массив testtype [] = { { 0 , 1 }, { 2 , 3 }, { 4 , 5 } }; распечатка ( массив ); /* анонимная функция задается как функция для foreach */ forEachInArray ( testtype , array , лямбда ( testtype , ( void * item ), { int temp = ( * ( testtype * ) item ). a ; ( * ( testtype * ) item ) .a = ( * ( testtype * ) item ) .b ; ( * ( testtype * ) item ) .b = temp ; return ( * ( testtype * ) item ); })); распечатка ( массив ); вернуть 0 ; }
Clang поддерживает анонимные функции, называемые блоками [54] , которые имеют форму:
^ тип_возврата ( параметры ) { тело_функции }
Тип блоков выше return_type (^)(parameters)
.
Используя вышеупомянутое расширение блоков и Grand Central Dispatch (libdispatch), код мог бы выглядеть проще:
#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 ( "ах ах ах \n " ); }; /* Передача в качестве параметра в другую функцию */ send_async ( dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 ), count_loop ); /* Непосредственный вызов */ count_loop (); вернуть 0 ; }
Код с блоками должен быть скомпилирован -fblocks
и связан с-lBlocksRuntime
C++11 поддерживает анонимные функции (технически функциональные объекты ), называемые лямбда-выражениями , [55] которые имеют форму:
[ захватывает ] ( параметры ) требуются спецификации ( необязательно ) { тело }
где " specs
" имеет форму " в указанном порядке; каждый из этих компонентов является необязательным". Если он отсутствует, тип возвращаемого значения выводится из операторов, как если бы это была функция с объявленным типом возвращаемого значения .specifiers exception attr trailing-return-type
return
auto
Это пример лямбда-выражения:
[]( int x , int y ) { return x + y ; }
C++11 также поддерживает замыкания , называемые здесь захватами. Захваты определяются в квадратных скобках [
и ]
в объявлении лямбда-выражения. Механизм позволяет захватывать эти переменные по значению или по ссылке. Следующая таблица демонстрирует это:
[] // Никаких захватов, лямбда неявно конвертируется в указатель на функцию. [ x , & y ] // x фиксируется по значению, а y фиксируется по ссылке. [ & ] // Любая внешняя переменная неявно фиксируется по ссылке, если она используется. [ = ] // Любая внешняя переменная неявно фиксируется по значению, если она используется. [ & , x ] // x захватывается по значению. Другие переменные будут захватываться по ссылке. [ = , & z ] // z захватывается по ссылке. Другие переменные будут фиксироваться по значению.
Переменные, захватываемые по значению, по умолчанию являются постоянными. Добавление mutable
после списка параметров делает их непостоянными.
C++14 и более новые версии поддерживают init-capture, например:
std :: unique_ptr < int > ptr = std :: make_unique < int > ( 42 ); [ ptr ]{ /* ... */ }; // назначение копирования удаляется для уникального указателя [ ptr = std :: move ( ptr )]{ /* ... */ }; // хорошо автоматический счетчик = [ я = 0 ]() изменяемый { возвращение я ++ ; }; // для изменения счетчика 'i' требуется изменяемый объект (); // 0 счетчик (); // 1 счетчик (); // 2
Следующие два примера демонстрируют использование лямбда-выражения:
std :: vector <int> some_list { 1 , 2 , 3 , 4 , 5 } ; _ целое число = 0 ; std :: for_each ( begin ( some_list ), end ( some_list ), [ & total ]( int x ) { total += x ; }); // Обратите внимание, что std::accumulate здесь будет лучшей альтернативой...
При этом вычисляется общее количество всех элементов в списке. Переменная total
сохраняется как часть замыкания лямбда-функции. Поскольку это ссылка на переменную стека total
, она может изменить свое значение.
std :: vector <int> some_list { 1 , 2 , 3 , 4 , 5 } ; _ целое число = 0 ; целое значение = 5 ; std :: for_each ( begin ( some_list ), end ( some_list ), [ & total , value , this ]( int x ) { total += x * value * this -> some_func (); });
Это приведет total
к сохранению как ссылки, но value
будет сохранено как копия.
Захват this
особенный. Его можно захватить только по значению, а не по ссылке. Однако в C++17 текущий объект можно захватить по значению (обозначается *this
) или по ссылке (обозначается this
). this
может быть захвачен только в том случае, если ближайшая охватывающая функция является нестатической функцией-членом. Лямбда будет иметь тот же доступ, что и член, создавший ее, с точки зрения защищенных/частных членов.
Если this
захватывается явно или неявно, также проверяется область видимости вложенных членов класса. Доступ к членам this
не требует явного использования this->
синтаксиса.
Конкретная внутренняя реализация может различаться, но ожидается, что лямбда-функция, которая захватывает все по ссылке, будет хранить фактический указатель стека функции, в которой она создана, а не отдельные ссылки на переменные стека. Однако, поскольку большинство лямбда-функций небольшие по размеру и локальны, они, скорее всего, являются кандидатами на встраивание и, следовательно, не требуют дополнительного хранилища для ссылок.
Если объект замыкания, содержащий ссылки на локальные переменные, вызывается после самой внутренней области блока его создания, поведение не определено .
Лямбда-функции — это функциональные объекты типа, зависящего от реализации; имя этого типа доступно только компилятору. Если пользователь желает использовать лямбда-функцию в качестве параметра, тип параметра должен быть типом шаблона, либо он должен создать std::function
или аналогичный объект для захвата значения лямбда. Использование ключевого auto
слова может помочь сохранить лямбда-функцию,
auto my_lambda_func = [ & ]( int x ) { /*...*/ }; auto my_onheap_lambda_func = new auto ([ = ]( int x ) { /*...*/ });
Вот пример хранения анонимных функций в переменных, векторах и массивах; и передавая их как именованные параметры:
#include <функционал> #include <iostream> #include <вектор> 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 = []( двойной x ) { return x ; }; decltype ( f0 ) fa [ 3 ] = { f0 , f1 , [] ( double x ) { return x * x ; }}; std :: vector < decltype ( f0 ) > fv = { f0 , f1 }; фв . push_back ([]( double x ) { return x * x ; }); for ( size_t i = 0 ; i < fv . size (); i ++ ) { std :: cout << fv [ i ]( 2.0 ) << std :: endl ; } for ( size_t 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 :: конец ; } std :: cout << eval ( f0 ) << std :: endl ; std :: cout << eval ( f1 ) << std :: endl ; std :: cout << eval ([]( double x ) { return x * x ; }) << std :: endl ; }
Лямбда-выражение с пустой спецификацией захвата ( []
) можно неявно преобразовать в указатель на функцию того же типа, с которым было объявлено лямбда-выражение. Итак, это законно:
auto a_lambda_func = []( int x ) -> void { /*...*/ }; void ( * func_ptr ) ( int ) = a_lambda_func ; func_ptr ( 4 ); //вызывает лямбду.
Начиная с C++17 , можно объявлять лямбду constexpr
, а начиная с C++20 — consteval
с обычной семантикой. Эти спецификаторы идут после списка параметров, например mutable
. Начиная с C++23 , лямбда также может быть, static
если у нее нет захватов. Спецификаторы static
и mutable
не разрешается комбинировать.
Кроме того, начиная с C++23, лямбда-выражение может быть рекурсивным через явное значение this
первого параметра:
auto fibonacci = []( this auto self , int n ) { return n <= 1 ? n : сам ( n - 1 ) + сам ( n - 2 ); }; Фибоначчи ( 7 ); // 13
В дополнение к этому в C++23 изменен синтаксис, так что круглые скобки можно опускать в случае лямбды, не принимающей аргументов, даже если лямбда имеет спецификатор. Также сделано так, что последовательность спецификаторов атрибутов, которая появляется перед списком параметров, лямбда-спецификаторами или спецификатором noException (один из них должен быть), применяется к оператору вызова функции или шаблону оператора типа замыкания. В противном случае это относится к типу оператора вызова функции или шаблону оператора. Раньше такая последовательность всегда применялась к типу оператора вызова функции или шаблону оператора типа замыкания, что делало, например, атрибут [[noreturn]]
невозможным для использования с лямбда-выражениями.
Библиотека Boost также предоставляет собственный синтаксис для лямбда-функций, используя следующий синтаксис: [56]
for_each ( a.begin ( ) , a.end ( ), std :: cout << _1 << ' ' ) ;
Начиная с C++14 , параметры функции лямбды могут быть объявлены с помощью auto
. Полученная лямбда называется общей лямбдой и, по сути, представляет собой анонимный шаблон функции, поскольку правила вывода типа автоматических параметров являются правилами вывода аргументов шаблона. Начиная с C++20 , параметры шаблона также могут быть объявлены явно с помощью следующего синтаксиса:
[ захватывает ] <tparams> требует ( необязательно ) ( параметры ) спецификации требует ( необязательно ) { тело } _ _
В C# поддержка анонимных функций расширилась благодаря различным версиям компилятора языка. Язык версии 3.0, выпущенный в ноябре 2007 года вместе с .NET Framework версии 3.5, имеет полную поддержку анонимных функций. [57] : 7–8 [58] : 26 C# называет их лямбда-выражениями , следуя исходной версии анонимных функций — лямбда-исчислению . [59] [57] : 7–8, 91 [58] : 91
// первое int — это тип x' // второе int — возвращаемый тип // <see href="http://msdn.microsoft.com/en-us/library/bb549151.aspx" />Func<int,int> foo = x => x * x;
Console.WriteLine(foo(7));
Хотя функция анонимна, ее нельзя присвоить неявно типизированной переменной, поскольку синтаксис лямбда может использоваться для обозначения анонимной функции или дерева выражений, и компилятор не может автоматически определить выбор. [57] : 101–103 Например, это не работает:
// НЕ скомпилируется! вар foo = ( int x ) => x * x ;
Однако лямбда-выражение может участвовать в выводе типа и может использоваться в качестве аргумента метода , например, для использования анонимных функций с возможностью Map, доступной с помощью System.Collections.Generic.List
(в ConvertAll()
методе):
// Инициализируем список: var Values = new List < int > () { 7 , 13 , 4 , 9 , 3 }; // Сопоставляем анонимную функцию со всеми элементами списка, возвращаем новый список var foo = values . ConvertAll ( d => d * d ) ; // результат переменной foo имеет тип System.Collections.Generic.List<Int32>
Предыдущие версии C# имели более ограниченную поддержку анонимных функций. C# v1.0, представленный в феврале 2002 года вместе с .NET Framework v1.0, обеспечивал частичную поддержку анонимных функций за счет использования делегатов . [57] : 6 C# называет их лямбда-выражениями , следуя исходной версии анонимных функций — лямбда-исчислению . [57] : 91 Эта конструкция чем-то похожа на делегаты PHP. В C# 1.0 делегаты подобны указателям на функции, которые ссылаются на явно названный метод внутри класса. (Но в отличие от PHP, имя не требуется во время использования делегата.) C# v2.0, выпущенный в ноябре 2005 года вместе с .NET Framework v2.0, представил концепцию анонимных методов как способа написания безымянных встроенных операторов. блоки, которые могут быть выполнены при вызове делегата. [57] : 6–7 C# 3.0 продолжает поддерживать эти конструкции, но также поддерживает конструкцию лямбда-выражения.
Этот пример компилируется в C# 3.0 и демонстрирует три формы:
общественный класс TestDriver { делегат int SquareDelegate ( int d ); static int Square ( int d ) { return d * d ; } static void Main ( string [] args ) { // C# 1.0: необходим исходный синтаксис делегата // инициализация именованным методом. SquareDelegate A = новый SquareDelegate ( Square ); Система . Консоль . WriteLine ( A ( 3 )); // C# 2.0: Делегат можно инициализировать с помощью // встроенного кода, называемого «анонимным методом». Этот // метод принимает int в качестве входного параметра. SquareDelegate B = делегат ( int d ) { return d * d ; }; Система . Консоль . WriteLine ( B ( 5 )); // С# 3.0. Делегат можно инициализировать // с помощью лямбда-выражения. Лямбда принимает целое число и возвращает целое число. // Тип x определяется компилятором. SquareDelegate C = x => x * x ; Система . Консоль . WriteLine ( C ( 7 )); // С# 3.0. Делегат, который принимает один ввод и // возвращает один вывод, также может быть неявно объявлен с типом Func<>. Система . Func < int , int > D = x => x * x ; Система . Консоль . WriteLine ( D ( 9 )); } }
В случае версии C# 2.0 компилятор C# берет блок кода анонимной функции и создает статическую закрытую функцию. Внутри функция, конечно, получает сгенерированное имя; это сгенерированное имя основано на имени метода, в котором объявлен делегат. Но это имя не раскрывается коду приложения, кроме как с помощью отражения . [57] : 103 В случае версии C# 3.0 применяется тот же механизм.
Используяфункцияключевое слово:
fn = function (){ // операторы };
Или используя функцию стрелки:
fn = () => { // операторы };fn = () => SingleExpression // SingleExpression возвращается неявно. Нет необходимости в фигурных скобках или ключевом слове return.fn = singleParam => { // если стрелочная функция имеет только один параметр, скобки // не нужны }fn = ( x , y ) => { // если стрелочная функция имеет ноль или несколько параметров, необходимо использовать скобки // операторы }
CFML поддерживает любые операторы в определении функции, а не просто выражения.
CFML поддерживает рекурсивные анонимные функции:
факториал = функция ( n ) { return n > 1 ? n * факториал ( n - 1 ) : 1 ; };
Анонимные функции CFML реализуют замыкание.
D использует встроенные делегаты для реализации анонимных функций. Полный синтаксис встроенного делегата:
делегат return_type ( аргументы ){ /*body*/ }
Если однозначно, тип возвращаемого значения и ключевое слово делегат можно опустить.
( x ){ return x * x ;} Deleate ( x ){ return x * x ;} // если требуется больше подробностей ( int x ) { return x * x ;} // если тип параметра не может быть выведен делегатом ( int x ){ return x * x ;} // то же самое делегат double ( int x ){ return x * x ;} // если тип возвращаемого значения необходимо принудительно указать вручную
Начиная с версии 2.0, D размещает замыкания в куче, если только компилятор не докажет, что в этом нет необходимости; ключевое scope
слово можно использовать для принудительного выделения стека. Начиная с версии 2.058 можно использовать сокращенную запись:
х => х * х ; ( int x ) => x * x ; ( Икс , y ) => Икс * y ; ( int x , int y ) => x * y ;
Анонимную функцию можно присвоить переменной и использовать следующим образом:
auto sqr = ( double x ) { return x * x ;}; двойной у = sqr ( 4 );
Dart поддерживает анонимные функции. [11]
вар sqr = ( x ) => x * x ; печать ( sqr ( 5 ));
или
print ((( x ) => x * x )( 5 ));
Delphi представила анонимные функции в версии 2009.
демо-версия программы ; введите TSimpleProcedure = ссылка на процедуру ; TSimpleFunction = ссылка на функцию ( const x : string ) : Integer ; вар x1 : TSimpleProcedure ; y1 : TSimpleFunction ; Begin x1 := процедура Begin Writeln ( 'Hello World' ) ; конец ; х1 ; //вызываем только что определенный анонимный метод y1 := функция ( const x : строка ) : целое начало Результат := длина ( x ) ; конец ; Writeln ( y1 ( 'бар' )) ; конец .
PascalABC.NET поддерживает анонимные функции, используя синтаксис лямбда.
начать вар n := 10000000 ; var pp := Диапазон ( 1 , n ) . Выберите ( x -> Rec ( Random () , Random ())) . Где ( p -> sqr ( p . Item1 ) + sqr ( p . Item2 ) < 1 ) . Граф / n * 4 ; Печать ( стр. ) ; конец .
Эликсир использует замыкание fn
для анонимных функций. [15]
сумма = fn ( a , b ) -> a + b конечная сумма . ( 4 , 3 ) #=> 7 квадрат = fn ( x ) -> x * x end Enum . карта [ 1 , 2 , 3 , 4 ], квадрат #=> [1, 4, 9, 16]
Erlang использует синтаксис анонимных функций, аналогичный синтаксису именованных функций. [16]
% Анонимная функция, привязанная к переменной Square Square = fun ( X ) -> X * X end . % Именованная функция с той же функциональностью Square ( X ) - > X * X.
Go поддерживает анонимные функции. [21]
foo := func ( x int ) int { return x * x } fmt . Println ( фу ( 10 ))
Haskell использует краткий синтаксис для анонимных функций (лямбда-выражений). Обратная косая черта должна напоминать λ.
\ х -> х * х
Лямбда-выражения полностью интегрированы с механизмом вывода типов и поддерживают весь синтаксис и функции «обычных» функций (за исключением использования нескольких определений для сопоставления с образцом, поскольку список аргументов указывается только один раз).
map ( \ x -> x * x ) [ 1 .. 5 ] — возвращает [1, 4, 9, 16, 25]
Все следующие эквивалентны:
f x y = x + y f x = \ y -> x + y f = \ x y -> x + y
В Haxe анонимные функции называются лямбда и используют синтаксис function(argument-list) expression;
.
var f = функция ( x ) return x * x ; ж ( 8 ); // 64 ( функция ( x , y ) возвращает x + y )( 5 , 6 ); // 11
Java поддерживает анонимные функции, называемые Lambda Expressions , начиная с JDK 8 . [60]
Лямбда-выражение состоит из списка формальных параметров, разделенных запятыми, заключенных в круглые скобки, маркера стрелки ( ->
) и тела. Типы данных параметров всегда можно опустить, как и круглые скобки, если имеется только один параметр. Тело может состоять из одного оператора или блока операторов. [61]
// без параметра () -> System . вне . println ( «Привет, мир!» ) // с одним параметром (данный пример — идентификационная функция). а -> а // с одним выражением ( a , b ) -> a + b // с явной информацией о типе ( длинный идентификатор , строковое имя ) -> "id: " + id + ", name:" + name // с блоком кода ( a , b ) -> { return a + b ; } // с несколькими операторами в теле лямбда-выражения. Ему нужен блок кода. // Этот пример также включает два вложенных лямбда-выражения (первое из них также является замыканием). ( id , defaultPrice ) -> { Необязательно < Продукт > Product = ProductList . транслировать (). фильтр ( p -> p . getId () == id ). НайтиПервый (); возврат товара . карта ( p -> p . getPrice ()). илиЕльсе ( Цена по умолчанию ); }
Лямбда-выражения преобразуются в «функциональные интерфейсы» (определяемые как интерфейсы, которые содержат только один абстрактный метод в дополнение к одному или нескольким методам по умолчанию или статическим методам), [61] , как в следующем примере:
Калькулятор общественного класса { интерфейс IntegerMath { int операция ( int a , int b ); по умолчанию IntegerMath swap () { return ( a , b ) -> операция ( b , a ); } } Private static int apply ( int a , int b , IntegerMath op ) { return op . операция ( а , б ); } public static void main ( String ... args ) { IntegerMath дополнение = ( a , b ) -> a + b ; Вычитание IntegerMath = ( a , b ) -> a - b ; Система . вне . println ( "40 + 2 = " + применить ( 40 , 2 , сложение )); Система . вне . println ( "20 - 10 =" + применить ( 20 , 10 , вычитание )); Система . вне . println ( "10 - 20 = " + применить ( 20 , 10 , вычитание . поменять местами ())); } }
IntegerMath
В этом примере объявлен функциональный интерфейс . Реализуемые лямбда-выражения IntegerMath
передаются методу, apply()
который должен быть выполнен. Методы по умолчанию, такие как swap
определение методов для функций.
В Java 8 появился еще один механизм, названный ссылкой на метод ( ::
оператор), для создания лямбда-выражения для существующего метода. Ссылка на метод не указывает количество или типы аргументов, поскольку они извлекаются из абстрактного метода функционального интерфейса.
IntBinaryOperator sum = Integer :: sum ;
В приведенном выше примере функциональный интерфейс IntBinaryOperator
объявляет абстрактный метод int applyAsInt(int, int)
, поэтому компилятор ищет метод int sum(int, int)
в классе java.lang.Integer
.
Анонимные классы лямбда-совместимых интерфейсов похожи, но не совсем эквивалентны лямбда-выражениям. Для иллюстрации в следующем примере anonymousClass
оба lambdaExpression
экземпляра IntegerMath
добавляют два параметра:
IntegerMathonymousClass = new IntegerMath ( ) { @Override public int Operation ( int a , int b ) { return a + b ; } }; IntegerMath лямбда-выражение = ( a , b ) -> a + b ;
Основное отличие здесь заключается в том, что лямбда-выражению не обязательно нужно выделять новый экземпляр для IntegerMath
, и оно может возвращать один и тот же экземпляр каждый раз при запуске этого кода. [62]
Кроме того, по крайней мере в реализации OpenJDK , лямбды компилируются для вызова динамических инструкций, при этом тело лямбды вставляется как статический метод в окружающий класс, [63] вместо того, чтобы полностью генерировать новый файл класса.
Лямбды Java 8 имеют следующие ограничения:
JavaScript / ECMAScript поддерживает анонимные функции.
предупреждение (( функция ( x ) { return x * x ; }) ( 10 ));
ES6 поддерживает синтаксис «стрелочной функции», где символ => отделяет список параметров анонимной функции от тела:
оповещение (( x => x * x )( 10 ));
Эта конструкция часто используется в Букмарклетах . Например, чтобы изменить заголовок текущего документа (видимый в строке заголовка его окна ) на его URL-адрес , может показаться, что работает следующий букмарклет.
документ . заголовок = местоположение . href ;
Однако, поскольку оператор присваивания возвращает значение (сам URL-адрес), многие браузеры фактически создают новую страницу для отображения этого значения.
Вместо этого можно использовать анонимную функцию, которая не возвращает значение:
( функция () { document . title = location . href ;})();
Оператор функции в первой (внешней) паре круглых скобок объявляет анонимную функцию, которая затем выполняется при использовании с последней парой круглых скобок. Это почти эквивалентно следующему, которое заполняет среду f
анонимной функцией.
вар f = функция () { документ . заголовок = местоположение . href ;}; е ();
Используйте void() , чтобы избежать появления новых страниц для произвольных анонимных функций:
void ( function () { return document . title = location . href ;}());
или просто:
void ( document.title = location.href ) ; _ _ _
В JavaScript есть синтаксические тонкости определения, вызова и оценки анонимных функций. Эти подсознательные нюансы являются прямым следствием оценки выражений в скобках. Это иллюстрируют следующие конструкции, которые называются выражением немедленно вызываемой функции :
( функция (){ ... }())
и
( функция (){ ... })()
Представляя " function(){ ... }
" by f
, формы конструкций представляют собой скобки внутри скобок (f())
и скобки , применяемые к скобкам (f)()
.
Обратите внимание на общую синтаксическую неоднозначность выражения в скобках, аргументов функции в скобках и круглых скобок вокруг формальных параметров в определении функции. В частности, JavaScript определяет ,
оператор (запятая) в контексте выражения в скобках. То, что синтаксические формы выражения и аргументов функции совпадают (без учета синтаксиса формальных параметров функции) – не простое совпадение! Если f
. не идентифицирован в приведенных выше конструкциях, они становятся (())
и ()()
. Первый не дает синтаксического намека на какую-либо резидентную функцию, но второй ДОЛЖЕН оценить первую скобку как функцию, чтобы быть законным JavaScript. (Кроме того: например, это ()
может быть ([],{},42,"abc",function(){}), если выражение оценивается как функция.)
Кроме того, функция является экземпляром объекта (аналогично объекты являются экземплярами функции), а скобки обозначения литерала объекта {}
для заключенного в скобки кода используются при таком определении функции (в отличие от использования new Function(...)
). В очень широком, нестрогом смысле (особенно учитывая, что глобальные привязки скомпрометированы), произвольную последовательность заключенных в скобки операторов JavaScript {stuff}
можно рассматривать как фиксированную точку
( функция (){( функция (){( ... {( функция (){ материал }() )} ... )}() )}() )
Правильнее, но с оговорками,
( функция () { материал }() ) ~= A_Fixed_Point_of ( функция () { функция возврата () { возврат ... { функция возврата () { материал }() } ... }() }() )
Обратите внимание на значение анонимной функции в следующих фрагментах JavaScript:
function(){ ... }()
без окружения ()
вообще не законно(f=function(){ ... })
не "забывает" f
глобально в отличие от(function f(){ ... })
В Julia анонимные функции определяются с использованием синтаксиса (arguments)->(expression)
:
Юлия> ж = х -> х * х ; ж ( 8 ) 64 Юлия> (( x , y ) -> x + y )( 5 , 6 ) 11
Kotlin поддерживает анонимные функции с синтаксисом {arguments -> expression}
,
val sum = { x : Int , y : Int -> x + y } sum ( 5 , 6 ) // возвращает 11 val Even = { x : Int -> x % 2 == 0 } Even ( 4 ) // возвращает истинный
Lisp и Scheme поддерживают анонимные функции с использованием конструкции «лямбда», которая является ссылкой на лямбда-исчисление . Clojure поддерживает анонимные функции со специальной формой «fn» и синтаксисом чтения #().
( лямбда ( аргумент ) ( * аргумент аргумент ))
В Common Lisp есть концепция лямбда-выражений. Лямбда-выражение записывается в виде списка с символом «лямбда» в качестве первого элемента. Затем список содержит список аргументов, документацию или объявления и тело функции. Лямбда-выражения можно использовать внутри лямбда-форм и со специальным оператором «функция».
( функция ( лямбда ( arg ) ( сделать что-нибудь arg )))
«функция» может быть сокращена до #'. Кроме того, существует лямбда -макрос , который расширяется до функциональной формы:
; использование резкой кавычки #' ( лямбда ( arg ) ( сделай что-нибудь arg )) ; используя лямбда-макрос: ( лямбда ( arg ) ( сделать что-нибудь arg ))
Одним из типичных применений анонимных функций в Common Lisp является передача их функциям более высокого порядка, таким как Mapcar , который применяет функцию к каждому элементу списка и возвращает список результатов.
( mapcar #' ( лямбда ( x ) ( * x x )) ' ( 1 2 3 4 ) ) ; -> (1 4 9 16)
Лямбда -форма в Common Lisp позволяет записать лямбда-выражение при вызове функции:
(( лямбда ( x y ) ( + ( sqrt x ) ( sqrt y ))) 10,0 12,0 )
Анонимным функциям в Common Lisp позже можно будет дать глобальные имена:
( setf ( символ-функция 'sqr ) ( лямбда ( x ) ( * x x ))) ; что позволяет нам вызывать его, используя имя SQR: ( sqr 10.0 )
Именованные функции Scheme — это просто синтаксический сахар для анонимных функций, привязанных к именам:
( определить ( какое-то имя arg ) ( сделать что-нибудь arg ))
расширяется (и эквивалентно) до
( определить какое-нибудь имя ( лямбда ( arg ) ( сделать что-нибудь arg )))
Clojure поддерживает анонимные функции через специальную форму «fn»:
( фн [х] ( + х 3 ))
Существует также синтаксис чтения для определения лямбды:
#( + % %2%3 ) ; Определяет анонимную функцию, которая принимает три аргумента и суммирует их.
Как и Scheme, «именованные функции» Clojure — это просто синтаксический сахар для лямбда-выражений, привязанных к именам:
( defn func [arg] ( + 3 arg ))
расширяется до:
( def func ( fn [arg] ( + 3 arg )))
В Lua (как и в Scheme) все функции анонимны. Именованная функция в Lua — это просто переменная, содержащая ссылку на объект функции. [64]
Таким образом, в Луа
функция foo ( x ) возвращает 2 * x конец
это просто синтаксический сахар для
foo = функция ( x ) возвращает 2 * x конец
Пример использования анонимных функций для сортировки в обратном порядке:
table.sort ( сеть , функция ( a , b ) возвращает a . name > b . name end )
Wolfram Language — это язык программирования Mathematica . Анонимные функции важны при программировании последних. Есть несколько способов их создания. Ниже приведены несколько анонимных функций, которые увеличивают число. Первый является наиболее распространенным. #1
ссылается на первый аргумент и &
отмечает конец анонимной функции.
#1 + 1 & Функция [ x , x + 1 ] x \ [ Функция ] x + 1
Так, например:
ж := #1 ^ 2 & ; ж [ 8 ] 64 #1 + #2 и [ 5 , 6 ] 11
Кроме того, в Mathematica есть добавленная конструкция для создания рекурсивных анонимных функций. Символ «#0» относится ко всей функции. Следующая функция вычисляет факториал входных данных:
Если [ #1 == 1 , 1 , #1 * #0 [ #1 -1 ]] &
Например, 6-факториал будет выглядеть так:
Если [ #1 == 1 , 1 , #1 * #0 [ #1 -1 ]] & [ 6 ] 720
Анонимные функции в MATLAB или Octave определяются с использованием синтаксиса @(argument-list)expression
. Любые переменные, которые не найдены в списке аргументов, наследуются из окружающей области и фиксируются по значению.
>> f = @( x ) x * x ; f ( 8 ) ans = 64 >> (@( x , y ) x + y )( 5 , 6 ) % Работает только в октаве ans = 11
В Maxima анонимные функции определяются с использованием синтаксиса lambda(argument-list,expression)
:
f : лямбда ([ x ], x * x ); ж ( 8 ); 64лямбда ([ x , y ], x + y )( 5 , 6 ); 11
Различные диалекты ML поддерживают анонимные функции.
Анонимные функции в OCaml — это функции без объявленного имени. Вот пример анонимной функции, которая умножает свои входные данные на два:
весело х -> х * 2
В этом примере fun — это ключевое слово, указывающее, что функция является анонимной. Мы передаем аргумент x и ->, чтобы отделить аргумент от тела. [65]
F# поддерживает анонимные функции [17] следующим образом:
( весело x -> x * x ) 20 // 400
Стандартное машинное обучение поддерживает следующие анонимные функции:
fn arg => arg * arg
Nim поддерживает многострочные анонимные функции с несколькими выражениями. [33]
var anon = proc ( var1 , var2 : int ): int = var1 + var2 утверждать anon ( 1 , 2 ) == 3
Многострочный пример:
var anon = func ( x : int ): bool = if x > 0 : result = true else : result = false утверждать анон ( 9 )
Анонимные функции могут передаваться в качестве входных параметров других функций:
var города = @[ "Франкфурт" , "Токио" , "Нью-Йорк" ] города . сортировка ( proc ( x , y : строка ): int = cmp ( x . len , y . len ) )
Анонимная функция — это, по сути, функция без имени.
Perl 5 поддерживает анонимные функции [37] следующим образом:
( sub { print "Мне позвонили\n" }) -> (); # 1. полностью анонимный, вызывается по мере создания мой $squarer = sub { мой $x = сдвиг ; $х * $х }; # 2. присвоено переменной суб карри { my ( $sub , @args ) = @_ ; return sub { $sub -> ( @args , @_ ) }; # 3. как возвращаемое значение другой функции } # пример каррирования в программировании на Perl sub sum { my $tot = 0 ; $tot += $_ для @_ ; $tot } # возвращает сумму своих аргументов my $curried = curry \& sum , 5 , 7 , 9 ; напечатайте $curried -> ( 1 , 2 , 3 ), "\n" ; # печатает 27 (= 5 + 7 + 9 + 1 + 2 + 3)
Другие конструкции принимают в качестве аргументов голые блоки , которые выполняют функцию, аналогичную лямбда-функциям с одним параметром, но не имеют того же соглашения о передаче параметров, что и функции — @_ не установлен.
мои @squares = map { $_ * $_ } 1 .. 10 ; # карты и grep не используют ключевое слово sub my @square2 = map $_ * $_ , 1 .. 10 ; # скобки не нужны для одного выражения мой @bad_example = карта { печать для @_ } 1 .. 10 ; # значения не передаются, как обычная функция Perl
До версии 4.0.1 в PHP не было поддержки анонимных функций. [66]
В PHP 4.0.1 появилась create_function
первоначальная поддержка анонимных функций. Этот вызов функции создает новую функцию со случайным именем и возвращает ее имя (в виде строки).
$foo = create_function ( '$x' , 'return $x*$x;' ); $bar = create_function ( " \$ x" , "return \$ x* \$ x;" ); эхо $foo ( 10 );
Список аргументов и тело функции должны быть заключены в одинарные кавычки или знаки доллара должны быть экранированы. В противном случае PHP предполагает, что " $x
" означает переменную $x
, и подставит ее в строку (несмотря на то, что она, возможно, не существует) вместо того, чтобы оставить " $x
" в строке. Для функций с кавычками или функций с большим количеством переменных может оказаться довольно утомительным проверять, соответствует ли тело предполагаемой функции тому, что интерпретирует PHP.
Каждый вызов create_function
создает новую функцию, которая существует для остальной части программы и не может быть удалена сборщиком мусора , поскольку память в программе используется необратимо. Если это используется для создания анонимных функций много раз, например, в цикле, это может вызвать такие проблемы, как раздувание памяти.
В PHP 5.3 добавлен новый класс Closure
и магический метод __invoke()
, который делает экземпляр класса вызываемым. [67]
$х = 3 ; $func = функция ( $z ) { возвращение $z * 2 ; }; эхо $func ( $x ); // печатает 6
В этом примере $func
является экземпляром Closure
и echo $func($x)
эквивалентен echo $func->__invoke($x)
. PHP 5.3 имитирует анонимные функции, но не поддерживает настоящие анонимные функции, поскольку функции PHP по-прежнему не являются объектами первого класса.
PHP 5.3 поддерживает замыкания, но переменные должны быть явно указаны как таковые:
$х = 3 ; $func = function () use ( & $x ) { $x *= 2 ; }; $функ (); эхо $х ; // печатает 6
Переменная $x
связана по ссылке, поэтому вызов $func
изменяет ее, и изменения видны вне функции.
Функции стрелок были представлены в PHP 7.4.
$х = 3 ; $func = fn ( $z ) => $z * 2 ; эхо $func ( $x ); // печатает 6
Logtalk использует следующий синтаксис для анонимных предикатов (лямбда-выражений):
{ FreeVar1 , FreeVar2 , ... } / [ LambdaParameter1 , LambdaParameter2 , ...] >> Цель
Простой пример без свободных переменных и с использованием предиката сопоставления списка:
| ?- мета :: map([ X , Y ] >> ( Y равно 2 * X ), [ 1 , 2 , 3 ], Ys ). Ys = [ 2 , 4 , 6 ]да
Также поддерживается каррирование. Приведенный выше пример можно записать так:
| ?- мета :: map([ X ] >> ([ Y ] >> ( Y равно 2 * X )), [ 1 , 2 , 3 ], Ys ). Ys = [ 2 , 4 , 6 ]да
Анонимные функции (в общем анонимные предикаты ) были введены в Visual Prolog в версии 7.2. [68] Анонимные предикаты могут захватывать значения из контекста. Если он создан в члене объекта, он также может получить доступ к состоянию объекта (путем захвата This
).
mkAdder
возвращает анонимную функцию, которая захватила аргумент X
в замыкании. Возвращаемая функция — это функция, которая добавляет X
к своему аргументу:
предложения mkAdder ( X ) = { ( Y ) = X + Y }.
Python поддерживает простые анонимные функции через лямбда-форму. [39] Исполняемое тело лямбды должно быть выражением и не может быть оператором, что является ограничением, ограничивающим его полезность. Значение, возвращаемое лямбда-выражением, является значением содержащегося выражения. Лямбда-формы можно использовать везде, где можно использовать обычные функции. Однако эти ограничения делают его очень ограниченной версией обычной функции. Вот пример:
>>> foo = лямбда x : x * x >>> foo ( 10 ) 100
В целом, соглашение Python поощряет использование именованных функций, определенных в той же области, что и анонимные функции в других языках. Это приемлемо, поскольку локально определенные функции реализуют всю мощь замыканий и почти так же эффективны, как использование лямбда-выражений в Python. В этом примере можно сказать, что встроенная степенная функция была каррирована :
>>> def make_pow ( n ): ... def фиксированная_экспонента_pow ( x ): ... return pow ( x , n ) ... return фиксированная_экспонента_pow ... >>> sqr = make_pow ( 2 ) >>> sqr ( 10 ) 100 >>> куб = make_pow ( 3 ) >>> куб ( 10 ) 1000
В R анонимные функции определяются с использованием синтаксиса function(argument-list)expression
, который с версии 4.1.0 имеет сокращение \
, похожее на Haskell.
> f <- функция ( x ) x * x ; f ( 8 ) [1] 64 > ( функция ( x , y ) x + y )( 5 , 6 ) [1] 11 > # Поскольку R 4.1.0 > ( \ ( x , y ) x + y )( 5 , 6 ) [1] 11
В Raku все блоки (даже связанные с if, while и т.п.) являются анонимными функциями. Блок, который не используется в качестве значения r , выполняется немедленно.
{ сказать «Мне позвонили» };
мой $squarer1 = -> $x { $x * $x }; № 2а. заостренный блок my $squarer2 = { $^x * $^x }; № 2б. twigil my $squarer3 = { my $x = сдвиг @_ ; $х * $х }; № 2в. Стиль Перл 5
sub add ( $m , $n ) { $m + $n } my $seven = add ( 3 , 4 ); мой $add_one = &add . предполагая ( m => 1); мой $eight = $add_one ( $seven );
мой $w = * - 1 ; # Объект WhatCode my $b = { $_ - 1 }; # та же функциональность, но как блок Callable
Ruby поддерживает анонимные функции, используя синтаксическую структуру, называемую блоком . В Ruby существует два типа данных для блоков. Proc
s ведут себя аналогично замыканиям , тогда как lambda
s ведут себя более аналогично анонимной функции. [42] При передаче методу блок в некоторых случаях преобразуется в Proc.
# Пример 1: # Чисто анонимные функции, использующие блоки. бывший = [ 16 . 2 , 24 . 1 , 48 . 3 , 32 . 4 , 8 . 5 ] => [ 16 . 2 , 24 . 1 , 48 . 3 , 32 . 4 , 8 . 5 ] пр . сортировка_по { | х | х - х . to_i } # Сортировка по дробной части, игнорируя целую часть. => [ 24 . 1 , 16 . 2 , 48 . 3 , 32 . 4 , 8 . 5 ] # Пример 2: # Функции первого класса как явный объект Proc - ex = Proc . new { помещает "Привет, мир!" } => #<Proc:0x007ff4598705a0@(irb):7> ex . звонок Привет , мир! => ноль # Пример 3: # Функция, которая возвращает объект лямбда-функции с параметрами def Multiple_of? ( п ) лямбда { | х | x % n == 0 } end => ноль Multiple_four = Multiple_of? ( 4 ) => #<Proc:0x007ff458b45f88@(irb):12 (лямбда)> Multiple_four . вызов ( 16 ) => true Multiple_four [ 15 ] => false
В Rust анонимные функции называются замыканиями. [69] Они определяются с использованием следующего синтаксиса:
|< параметр - имя > : < тип >| -> < возврат - тип > { < тело > };
Например:
пусть ж = | х : i32 | -> i32 { х * 2 };
Однако с помощью вывода типа компилятор может определить тип каждого параметра и тип возвращаемого значения, поэтому приведенную выше форму можно записать как:
пусть ж = | х | { х * 2 };
При замыканиях с одним выражением (т. е. телом с одной строкой) и неявным возвращаемым типом фигурные скобки можно опустить:
пусть ж = | х | х * 2 ;
Замыкания без входного параметра записываются так:
пусть f = || распечататьлн! ( "Привет, мир!" );
Замыкания могут передаваться в качестве входных параметров функций, которые ожидают указатель на функцию:
// Функция, которая принимает указатель функции в качестве аргумента и вызывает ее // со значением `5`. fn apply ( f : fn ( i32 ) -> i32 ) -> i32 { // Нет точки с запятой, чтобы указать неявный возврат f ( 5 ) } fn main () { // Определение замыкания let f = | х | х * 2 ; распечататьлн! ( "{}" , применить ( f )); // 10 println! ( "{}" , f ( 5 )); // 10 }
Однако для описания того, как фиксируются значения в теле замыкания, могут потребоваться сложные правила. Они реализованы с использованием черт Fn
, FnMut
и FnOnce
: [70]
Fn
: замыкание фиксируется по ссылке ( &T
). Они используются для функций, которые все еще можно вызывать, если у них есть только ссылочный доступ (с &
) к своей среде.FnMut
: замыкание фиксируется по изменяемой ссылке ( &mut T
). Они используются для функций, которые могут быть вызваны, если у них есть доступ к изменяемой ссылке (с &mut
) к их среде.FnOnce
: замыкание фиксируется по значению ( T
). Они используются для функций, которые вызываются только один раз.Благодаря этим особенностям компилятор будет захватывать переменные наименее ограничительным образом. [70] Они помогают управлять тем, как значения перемещаются между областями действия, что очень важно, поскольку Rust следует конструкции жизненного цикла, чтобы гарантировать, что значения «заимствованы» и перемещены предсказуемым и явным образом. [71]
Ниже показано, как можно передать замыкание в качестве входного параметра, используя этот Fn
признак:
// Функция, которая принимает значение типа F (который определяется как // универсальный тип, реализующий признак `Fn`, например замыкание) // и вызывает его со значением `5`. fn apply_by_ref < F > ( f : F ) -> i32 где F : Fn ( i32 ) -> i32 { f ( 5 ) } fn main () { let f = | х | { печать! ( «Я получил значение: {}» , x ); х * 2 }; // Применяет функцию перед печатью возвращаемого значения println! ( "5 * 2 = {}" , apply_by_ref ( f )); } // ~~ Вывод программы ~~ // Я получил значение: 5 // 5 * 2 = 10
Предыдущее определение функции также можно для удобства сократить следующим образом:
fn apply_by_ref ( f : impl Fn ( i32 ) -> i32 ) -> i32 { f ( 5 ) }
В Scala анонимные функции используют следующий синтаксис: [72]
( x : Int , y : Int ) => x + y
В определенных контекстах, например, когда анонимная функция является параметром, передаваемым другой функции, компилятор может определить типы параметров анонимной функции, и они могут быть опущены в синтаксисе. В таких контекстах также можно использовать сокращение анонимных функций, используя символ подчеркивания для введения безымянных параметров.
список значений = Список ( 1 , 2 , 3 , 4 ) список . уменьшитьLeft ( ( x , y ) => x + y ) // Здесь компилятор может сделать вывод, что оба типа x и y — Int. // Таким образом, аннотации типов для параметров анонимной функции не требуются. список . уменьшитьLeft ( _ + _ ) // Каждое подчеркивание обозначает новый безымянный параметр в анонимной функции. // Это приводит к еще более короткому эквиваленту анонимной функции, указанной выше.
В Smalltalk анонимные функции называются блоками и вызываются (вызываются) путем отправки им сообщения «значение». Если необходимо передать несколько аргументов, необходимо использовать сообщение «value:...value:» с соответствующим количеством аргументов-значений.
Например, в GNU Smalltalk
ст > ж := [ : х | х * х ] . значение f : 8 . 64 ст> [ : x : y | x + y ] значение: 5 значение: 6 . 11
Блоки Smalltalk технически являются замыканиями, что позволяет им выйти за рамки своей определяющей области и по-прежнему ссылаться на объявленные в них переменные.
ст > ж := [ : а | [ : н | a + n ]] значение: 100 . BlockClosure «возвращает внутренний блок, который добавляет 100 (захваченных в переменной « a » ) к своему аргументу». значение st> f : 1 . 101 ст> значение f : 2 . 102
В Swift анонимные функции называются замыканиями. [46] Синтаксис имеет следующий вид:
{ ( параметры ) -> returnType в операторе }
Например:
{ ( s1 : String , s2 : String ) -> Bool в ответ s1 > s2 }
Для краткости и выразительности типы параметров и тип возвращаемого значения можно опустить, если их можно вывести:
{ s1 , s2 взамен s1 > s2 } _
Аналогичным образом, Swift также поддерживает неявные операторы возврата для замыканий с одним оператором:
{ s1 , s2 в s1 > s2 }
Наконец, имена параметров также можно опустить; если они опущены, ссылки на параметры используются с использованием сокращенных имен аргументов, состоящих из символа $, за которым следует их позиция (например, $0, $1, $2 и т. д.):
{ $0 > $1 }
В Tcl применение анонимной функции возведения в квадрат к 2 выглядит следующим образом: [73]
apply { x {expr { $x * $x }}} 2 # возвращает 4
В этом примере участвуют два кандидата на роль функции в Tcl. Самый общий вариант обычно называется префиксом команды , и если переменная f содержит такую функцию, то способ выполнения приложения функции f ( x ) будет следующим:
{ * } $f $x
где {*}
находится префикс расширения (новое в Tcl 8.5). Префикс команды в приведенном выше примере — apply. {x {expr {$x*$x}}}
Имена команд можно привязать к префиксам команд с помощью команды interp alias
. Префиксы команд поддерживают каррирование . Префиксы команд очень распространены в API Tcl .
Другой кандидат на роль «функции» в Tcl обычно называется лямбда и появляется как {x {expr {$x*$x}}}
часть приведенного выше примера. Это та часть, которая кэширует скомпилированную форму анонимной функции, но ее можно вызвать только путем передачи команде apply
. Лямбды не поддерживают каррирование, если только они не объединены с apply
префиксом команды. Лямбды редко встречаются в API Tcl.
В Vala анонимные функции поддерживаются как лямбда-выражения. [74]
делегат int IntOp ( int x , int y ); void main () { IntOp foo = ( x , y ) => x * y ; стандартный вывод . printf ( "%d \n " , foo ( 10 , 5 )); }
Visual Basic .NET 2008 представил анонимные функции через лямбда-форму. В сочетании с неявной типизацией VB обеспечивает экономичный синтаксис для анонимных функций. Как и в Python, в VB.NET анонимные функции должны быть определены в одной строке; они не могут быть составными утверждениями. Более того, анонимная функция в VB.NET действительно должна быть VB.NET Function
— она должна возвращать значение.
Dim foo = Функция ( x ) x * x Консоль . WriteLine ( фу ( 10 ))
В Visual Basic.NET 2010 добавлена поддержка многострочных лямбда-выражений и анонимных функций без возвращаемого значения. Например, функция для использования в потоке.
Dim t As New System . Резьба . Thread ( Sub () For n As Integer = от 0 до 10 'Считаем до 10 Console.WriteLine ( n ) ' Распечатываем каждое число Next End Sub ) t . Начинать ()
Лямбда-исчисление ... было введено Алонзо Чёрчем в 1930-х годах как точное обозначение теории анонимных функций.
Цитата — это анонимная функция (значение, обозначающее фрагмент кода), которую можно использовать как значение и вызывать с помощью фундаментальных комбинаторов.