Анонимна функция
Тази статия се нуждае от подобрение. Необходимо е: уикифициране и проверка на превода. Ако желаете да помогнете на Уикипедия, използвайте опцията редактиране в горното меню над статията, за да нанесете нужните корекции. |
Тази статия се нуждае от вниманието на редактор с по-задълбочени познания. Ако смятате, че имате необходимите знания, подобрете тази страница. |
Анонимна функция (или т.нар. функционален литерал или ламбда функция) в програмирането е дефинирана функция, която не е обвързана с идентификатор, тоест не е именувана.
Най-често анонимните функции могат да бъдат използвани:[1]
- като аргументи, които се прехвърлят към функции от по-висок ред, или
- за построяване на резултата на функция от по-висок ред, която трябва да върне функция.
Ако дадена функция се използва само веднъж или ограничен брой пъти, е подходящо да бъде създадена като анонимна, тъй като това я прави синтактично по-лека, отколкото именувана функция. Във функционалните езици и други езици с функции от първи клас анонимните функции се използват навсякъде, където те изпълняват същата роля за вида на функцията, както правят литералите за други типове данни.
Произходът на анонимните функции е свързан с работата на Алонсо Чърч и неговата теория за ламбда смятане, (създадена преди ерата на компютрите), в която всички функции са анонимни.[2] В няколко програмни езика анонимните функции се представят чрез използването на ключовата дума ламбда, а анонимните функции често са посочени като ламбди или ламбда абстракции. Анонимните функции се въвеждат за пръв път в програмния език Lisp през 1958 г. Оттогава непрекъснато се увеличава броя на съвременните езици, които поддържат анонимни функции.
Анонимните функции са форма на вложена функция, в която е позволен достъпа до променливи в обхвата на съдържащата функция. Ето защо анонимните функции трябва да бъдат имплементирани чрез използването на затваряне. За разлика от именуваните вложени функции, те не могат да бъдат рекурсивни без помощта на т.нар. оператор на неподвижната точка (т.нар. анонимна неподвижна точка или анонимна рекурсия).[3]
Приложение
[редактиране | редактиране на кода]Анонимните функции могат да се използват за съдържащи функционалност, които не се нуждаят от наименуване и имат възможност за използване за кратък период от време. Някои първични примери включват затваряне и обработване.
Употребата на анонимните функции е въпрос на стил. Използването им никога не е единственият начин за решаване на проблем: всяка анонимна функция може да бъде дефинирана като именувана функция и наричана по име. Някои програмисти използват анонимните функции за капсулиране на специфични еднократни кодове, за да не се усложнява основния код с много едноредови нормални функции.
В някои програмни езици, анонимните функции често се имплементират за всяка специфична цел като свързващи събития за обратно извикване, или за представяне на функциите за конкретни стойности, което може да е по-ефикасно, по-четливо, и по-малко податливо на грешки от използваните по общи именувани функции.
Целият код в следващата секция е написан на Python 2.x (not 3.x).
В Java анонимните функции започват с JDK 8[4] и са познати като ламбда изрази.
Ламбда изразите се състоят от разделен със запетая списък на формалните параметри затворен в скоби, стрела знак (->), и тяло. Типът на данни на параметрите винаги може да бъде пропуснат, както и скобите, ако има само един параметър. Тялото може да се състои от едно твърдение или блок от твърдения.
// with no parameter
() -> System.out.println("Hello, world.")
// with a single parameter (This example is an identity function).
a -> a
// with a single expression
(a, b) -> a + b
// with explicit type information
(long id, String name) -> "id: " + id + ", name:" + name
// with a code block
(a, b) ->{ return a + b; }
// with multiple statements in the lambda body. It requires a code block.
// This example also includes two nested lambda expressions (the first one is also a closure).
(id, defaultPrice) ->{
Optional<Product> product = productList.stream().filter(p -> p.getId() == id).findFirst();
return product.map(p -> p.getPrice()).orElse(defaultPrice);
}
Ламбда изразите се превръщат във „функционални интерфейси“ (дефинират се като интерфейси, които съдържат само един абстрактен метод в допълнение към един или повече по подразбиране или статични методи[5]), както е показано в следните примери:
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()));
}
}
В този пример е деклариран функционалният интерфейс наречен IntegerMath. Ламбда изразите, които имплементират IntegerMath се предават на метода apply(), за да бъде изпълнен. Методи по подразбиране като swap дефинират методи за функции.
Сортиране
[редактиране | редактиране на кода]При сортиране по нестандартен начин може да бъде по-лесно да се използва логическо сравнение в анонимна функция вместо да се създава именувана функция. Повечето езици позволяват създаването на сортираща функция, която имплементира алгоритъм за сортиране за различни обекти. Тази функция обикновено приема името произволна функция за сравнение, това е взимане на 2 обекта и функцията показва дали те са равни или единият е „по-голям“ или „по-малък“от другия (обикновено се връща отговор като отрицателно, нула, или положително число).
Сортиране на списък от низове по дължина на низа:
>>> a = ['house', 'car', 'bike']
>>> a.sort(lambda x,y: cmp(len(x), len(y)))
>>> print(a)
['car', 'bike', 'house']
Анонимната функция в примера е ламбда израз:
lambda x,y: cmp(...)
Анонимната функция приема два аргумента, х и у, и връща сравнението между тях използвайки вградена функция cmp(). Друг пример може да бъде сортиране на обекти в списък по името на техния клас (в Python всички имат клас):
>>> a = [10, 'number', 11.2]
>>> a.sort(lambda x,y: cmp(x.__class__.__name__, y.__class__.__name__))
>>> print(a)
[11.2, 10, 'number']
Забележете че във версия 11.2 има клас „float“, а във версия 10 има клас „int“, и ‚число‘ има клас „str“. Сортиращият параметър е „float“, „int“, тогава „str“.
Затваряне (информатика)
[редактиране | редактиране на кода]Затварянето в програмирането е такава функция, която запазва обектите дефинирани в нейната среда на видимост, поне докато те се използват или са реферирани. Затварянията обикновено се ползват в езици, в които функциите са обекти от първи клас, с други думи езици, които позволяват функции да бъдат аргументи, да бъдат връщани като резултат от други функции от по-висок ред, да бъдат запазвани като променливи, подобно на примитивни типове (напр. int или string).
В следващия пример чрез анонимна функция се сравнява стойността на входящата променлива х с дадена променлива "threshold":
def comp(threshold):
return lambda x: x < threshold
Този пример може да се използва като генератор на сравнителни функции:
>>> 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
Би било непрактично да се създава нова функция за всяко възможно сравнение. Понякога е неудобно да се запазва стойността на прага като променлива за по-нататъшна употреба. Без значение каква е причината да се използва затваряне, анонимната функция е тази, която съдържа функционалността да се прави сравнение.
Къринг (Currying)
[редактиране | редактиране на кода]Къринг е техника на промяна на логиката на изчисление от една функция, която приема множество от аргументи (или кортеж) към серия от функции, които приемат по точно един аргумент. В този случай за пример е дадена трансформация на функция за деление с произволно цяло число към функция за деление с точно определено цяло число.
>>> def divide(x, y):
... return x / y
>>> def divisor(d):
... return lambda x: divide(x, d)
>>> half = divisor(2)
>>> third = divisor(3)
>>> print half(32), third(32)
16 10
>>> print half(40), third(40)
20 13
Въпреки че анонимни функции рядко се използват при къринг, те все пак имат своето място. В горния пример функцията „devisor“ генерира функции с променлив делител. Анонимните функции „half“ и „third“ кърират функцията „divide“ с фиксиран делител (2 и 3).
Функцията „devisor“ също така е затваряне, понеже запазва определена стойност на променливата „d“.
Функции от по-висок порядък
[редактиране | редактиране на кода]Python 2.x включва няколко функции, които използват анонимните функции като аргумент. Тази секция описва някои от тях.
Карта (Map)
[редактиране | редактиране на кода]Тази функция използва всеки елемент от списък, извиквайки функция за него. Следният пример показва функция, която повдига на квадрат всеки елемент от даден списък.
>>> a = [1, 2, 3, 4, 5, 6]
>>> print map(lambda x: x*x, a)
[1, 4, 9, 16, 25, 36]
Анонимната функция (отгоре) приема аргумент и го умножава по себе си (повдига го на квадрат).
Филтър
[редактиране | редактиране на кода]Филтърната функция връща всички елементи от списък, които са оценени като верни, когато преминават през определена функция.
>>> a = [1, 2, 3, 4, 5, 6]
>>> print filter(lambda x: x % 2 == 0, a)
[2, 4, 6]
Тази функция проверява дали подаденият аргумент е с четна стойност.
Фолд (fold, обвиваща) функция
[редактиране | редактиране на кода]Фолд функцията минава през всички елементи от списъка (обикновено от ляво надясно), натрупвайки стойност от това преминаване. Най-често се използва, за да комбинира всички елементи от списък в единична стойност, като например:
>>>> а = [1, 2, 3, 4, 5]
>>>> print reduce(lambda x,y: x*y, a)
120
Този код пресмята следното:
(((1 Х 2) Х 3) Х 4) Х 5) = 120
В този случай финцията изпълнява умножение на 2 аргумента.
Резултатът от тази функция не е задължително да е единична стойност – всъщност и функцията map и функцията filter могат да бъдат създадени чрез функцията fold. При функцията map, стойността, която е акумулирана е нов списък, съдържащ резултатите от използването на функцията на всеки елемент от оригиналния списък. При filter функцията, стойността, която се акумулира е нов списък, който съдържа само елементите, които отговарят на определено условие.
Списък с езици
[редактиране | редактиране на кода]Списъкът отдолу съдържа програмни езици, които поддържат напълно безименните анонимни функции, съпортват някои варианти на анонимни функции и такива, които не поддържат анонимни функции.
Тази таблица показва някои основни тенденции. Първо, езиците, които не поддържат анонимни функции са следните: C, Pascal, Object Pascal, Java – всичките те са статично ориентирани езици. Това обаче не означава, че статичните езици са неспособни да поддържат анонимни функции. Например, ML езиците са от статичен тип и в основата си съдържат анонимни функции, също така и Delphi, който е „диалект“ на обектно ориентирания Pascal, е бил разширен, за да поддържа тези функции. Второ, езиците, които „третират“ анонимните фунции като фунции от първи клас – Dylan, JavaScript, Lisp, Scheme, ML, Haskell, Python, Ruby, Perl, в общи линии поддържат анонимни функции, така че тези функции могат да бъдат дефинирани и използвани лесно при други видове данни. Новият C++ стандарт добавя тези функции към C++, въпреки че той е общоприет статичен език.
Език | Поддържа |
---|---|
ActionScript | Да |
Ada | Не |
ALGOL 68 | Да |
Brainfuck | Не |
Bash | Частично |
C | Не |
C# | Да |
C++ | Да |
Clojure | Да |
COBOL | Не |
CURL | Да |
D | Да |
Dart | Да |
Delphi | Да |
Dylan | Да |
Eiffel | Да |
Erlang | Да |
F Sharp | Да |
Factor | Да |
FORTRAN | Не |
Frink | Да |
Go | Да |
Gosu | Да[6] |
Groovy | Да[7] |
Haskell | Да |
Haxe | Да |
Java | Да |
JavaScript | Да |
Julia | Да |
Lisp | Да |
Logtalk | Да |
Lua | Да |
MUMPS | Не |
Mathematica | Да |
Maple | Да |
MATLAB | Да |
Maxima | Да |
OCaml | Да |
Octave | Да |
Object Pascal | Частично |
Objective-C (Mac OS X 10.6+) | Да |
Pascal | Не |
Perl | Да |
PHP | Да |
PL/I | Не |
Python | Частично |
R | Да |
Racket | Да |
Rexx | Не |
RPG | Не |
Ruby | Да |
Rust | Да |
Scala | Да |
Scheme | Да |
Smalltalk | Да |
Standard ML | Да |
Swift | Да |
TypeScript | Да |
Tcl | Да |
Vala | Да |
Visual Basic .NET v9 | Да |
Visual Prolog v 7.2 | Да |
Wolfram Language | Да |
Примери
[редактиране | редактиране на кода]Множество езици поддържат анонимни функции, или нещо подобно.
Поддръжката на анонимни функции в C# се е задълбочила през различните версии на компилатора на езика. C # езикът v3.0 В, излязъл през ноември 2007 г. с .NET Framework-а v3.5, има пълна поддръжка на анонимни функции. В C # те се наричат „ламбда изрази“ („lambda expressions“), следвайки първоначалната версия на анонимни функции, ламбда смятане (lambda calculus).[8]
Ламбда изразите представляват анонимни функции, които съдържат изрази или последователност от оператори. Всички ламбда изрази
използват ламбда оператора =>, който може да се чете като „отива в“. Идеята за ламбда изразите в C# е заимствана от функционалните езици
(например Haskell, Lisp, Scheme, F# и др.). Лявата страна на ламбда оператора определя входните параметри на анонимната функция, а дясната страна представлява израз или последователност от оператори, която работи с входните параметри и евентуално връща някакъв резултат.[9]
// <see href="http://msdn.microsoft.com/en-us/library/bb549151.aspx" />
Func<int,int> foo = x => x*x;
Console.WriteLine(foo(7));
Докато функцията е анонимна, не може да се възложи на имплицитно зададена променлива, защото синтаксисът на ламбда може да се използва за означаване на анонимна функция или за „expression tree“, и изборът не може автоматично да се реши от компилатора. Например това не работи:
// will Not Compile!
var foo = (int x) => x*x;
Въпреки това, един ламбда израз може да вземе участие в type inference и може да се използва като аргумент на метод, например да използват анонимни функции с въжможностите на Map (Карта) от System.Collections.Generic.List (в ConvertAll () метод):
// Initialize the list:
var values = new List<int>() { 7, 13, 4, 9, 3 };
// Map the anonymous function over all elements in the list, return the new list
var foo = values.ConvertAll(d => d*d);
// the result of the foo variable is of type System.Collections.Generic.List<Int32>
Предишните версии на C# са имали по-ограничена поддръжка на анонимните функции. C# v1.0, въвежда през февруари 2002 г. с .NET Framework v1.0, предоставянето на частична поддръжка на анонимните функции чрез използването на делегати (delegates). Тази конструкция е в известна степен подобна на PHP делегатите. В C# 1.0, делегатите са като указатели на функции, които се отнасят до определен метод в рамките на един клас. (Но за разлика от PHP името не се изисква в момента на използване на делегата.) C# v2.0, издаден през ноември 2005 г. с .NET Framework v2.0, въвежда концепцията за анонимни методи, като начин да пишат неименувани инлайн блокове изявление (inline statement blocks), които могат да бъдат изпълнени чрез извикване на делегат (delegate invocation). C# 3.0 продължава да поддържа тези конструкции, но също така поддържа и експресивната ламбда конструкция.
Този пример ще се компилира в C# 3.0, и показва трите форми:
public class TestDriver
{
delegate int SquareDelegate(int d);
static int Square(int d)
{
return d*d;
}
static void Main(string[] args)
{
// C# 1.0: Original delegate syntax required
// initialization with a named method.
SquareDelegate A = new SquareDelegate(Square);
System.Console.WriteLine(A(3));
// C# 2.0: A delegate can be initialized with
// inline code, called an "anonymous method." This
// method takes an int as an input parameter.
SquareDelegate B = delegate(int d) { return d*d; };
System.Console.WriteLine(B(5));
// C# 3.0. A delegate can be initialized with
// a lambda expression. The lambda takes an int, and returns an int.
// The type of x is inferred by the compiler.
SquareDelegate C = x => x*x;
System.Console.WriteLine(C(7));
// C# 3.0. A delegate that accepts a single input and
// returns a single output can also be implicitly declared with the Func<> type.
System.Func<int,int> D = x => x*x;
System.Console.WriteLine(D(9));
}
}
В случай че версията на C# е 2.0, С# компилаторът взима блока код на анонимната функция и създава static private функция. Вътрешно, функцията получава генерирано име, разбира се, това генерирано име се основава на името на метода, в който се декларира делегат. Но името не е изложено в кода на приложението, освен с помощта на „reflection“
В случай, че версията на C# е 3.0, се прилага същия механизъм.
javascript:(function(){document.title=location.href;})();
JavaScript
[редактиране | редактиране на кода]JavaScript/ECMAScript поддържа анонимни функции.
alert((function(x){
return x*x;
})(10));
В ES6:
alert((x => x*x)(10));
Тази технология се използва по-често в Bookmarklets. За пример, за да сменим заглавието на текущия документ (видим в главния бар на прозореца) в URL, в следващата отметка може помогне.
javascript:document.title=location.href;
Въпреки това, като зададена задача връща (собственото URL), много браузъри създават нова страница, за да изведат тази стойност. Вместо анонимна функция, която не връща стойност, може да се използва:
javascript:(function(){document.title=location.href;})();
Отчетът за функция в първата (външна) двойка скоби заявява анонимна функция, която след това се изпълнява, когато се използва с последната двойка скоби. Това е почти еквивалентна на следната информация, която е обкръжена с f за разлика от анонимна функция.
javascript:var f = function(){document.title=location.href;}; f();
Използване на void() за избягване на нови страници за произволни анонимни функции:
javascript:void(function(){return document.title=location.href;}());
или просто:
javascript:void(document.title=location.href);
Julia
[редактиране | редактиране на кода]
В Julia програмирането анонимните функции са дефинирани с използването на синтаксиса(arguments)->(expression)
,
julia> f = x -> x*x; f(8)
64
julia> ((x,y)->x+y)(5,6)
11javascript:void(document.title=location.href);
Lisp
[редактиране | редактиране на кода]Lisp и Scheme поддържат анонимните функции използвайки „ламбда“ конструкции, което е препратка към lambda calculus.
(lambda (arg) (* arg arg))
Scheme
[редактиране | редактиране на кода]Интересно е че, именуваните функции на Scheme е синтактична захар за анунимна функция обвързана с имена:
(define (somename arg)
(do-something arg))
разширява (и е еквивалент) на
(define somename
(lambda (arg)
(do-something arg)))
Clojure
[редактиране | редактиране на кода]Clojure поддържа анонимни функции чрез „Fn“ в специална форма:
(fn [x] (+ x 3))
Има и синтаксис за определяне на ламбда:
# (+ % %2 %3); Defines an anonymous function that takes three arguments and sums them.
Също като Scheme, именуваните функции на Clojure са синтактична захар за анонимна функция, обвързана с имена:
(defn func [arg] (+ 3 arg))
се разширява, до:
(def func (fn [arg] (+ 3 arg)))
Lua
[редактиране | редактиране на кода]В Lua всички функции са анонимни. „Именуваната функция“ в Lua е просто една променлива, която държи референция към обектна функция.[10]
По този начин в Lua:
function foo(x) return 2*x end
e просто синтактична захар за:
foo = function(x) return 2*x end
Пример за използване на анонимни функции за сортиране в обратен ред:
table.sort(network, function(a,b)
return a.name > b.name
end)
Вижте също
[редактиране | редактиране на кода]Източници
[редактиране | редактиране на кода]- ↑ "Higher order functions". learnyouahaskell.com. Посетен на 3 декември 2014.
- ↑ Fernandez, Maribel (2009), Models of Computation: An Introduction to Computability Theory, Undergraduate Topics in Computer Science, Springer Science & Business Media, p. 33, ISBN 9781848824348,
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 Recursions". CS3110 Spring 2012 :: Data Structures and Functional Programming. Cornell University – Computer Science. Посетен на 3 декември 2014.
- ↑ www.oracle.com
- ↑ The Java Tutorials: Lambda Expressions, docs.oracle.com
- ↑ Gosu Documentation // Посетен на 4 март 2013.
- ↑ Groovy Documentation // Архивиран от оригинала на 2012-05-22. Посетен на 29 май 2012.
- ↑ C# Language Specification 5.0 (спецификация docx файл) // microsoft.com, 2013-07-06. p. 123. Посетен на 2023-10-19. 5.3.3.29 Anonymous functions (C# Language Specification 4.0) (на английски)
- ↑ Светлин Наков, Веселин Колев и колектив, "Въведение в програмирането със C# „. Велико Търново, Фабер, 2011. ISBN 978-954-400-527-6. – глава 22, стр.922 – 928
- ↑ "Programming in Lua – More and Functions". Архивирано от оригинала на 14 май 2008. Посетен на 25 април 2008.
Тази страница частично или изцяло представлява превод на страницата Anonymous function в Уикипедия на английски. Оригиналният текст, както и този превод, са защитени от Лиценза „Криейтив Комънс – Признание – Споделяне на споделеното“, а за съдържание, създадено преди юни 2009 година – от Лиценза за свободна документация на ГНУ. Прегледайте историята на редакциите на оригиналната страница, както и на преводната страница, за да видите списъка на съавторите.
ВАЖНО: Този шаблон се отнася единствено до авторските права върху съдържанието на статията. Добавянето му не отменя изискването да се посочват конкретни източници на твърденията, които да бъдат благонадеждни. |