Анонимные функции c: C#. Делегаты. Анонимные функции и методы. Примеры кода

Содержание

Kotlin. Анонимные функции

Статья проплачена кошками — всемирно известными производителями котят.

Если статья вам понравилась, то можете поддержать проект.

В Kotlin часто используется особый вид функций — анонимные функции. Функции можно не давать имя после ключевого слова fun и она станет анонимной.

val a: (Int) -> Int = fun(i: Int) = i + 3
println(a(4)) // 7

Анонимная функция прибавляет к заданному значению число 3.

В некоторых случаях анонимные функции удобны и полезны. Например, мы можем добавить новые правила к существующим функциям.

В стандартной библиотеке есть функция count() для подсчёта числа символов в строке.

val charNumber = "Васька".count()
println(charNumber) // выводит 6

Есть перегруженная версия функции, в которой можно вызвать анонимную функцию в качестве аргумента. Зададим новое правило — подсчитать количество символов а в этом же слове.

val charNumber = "Васька".
count({letter -> letter == 'а'}) println(charNumber) // выводит 2

Функция count() использует анонимную функцию, чтобы решить, как считать символы в строке. Она последовательно передаёт анонимной функции символ за символом, и если та вернёт истинное значение для заданного символа, общий счёт нужных символов увеличивается на единицу. Как только будет проверен последний символ, функция count() возвратит итоговое значение. Это одно из применений анонимных функций.

Если заглянуть под капот, то увидим, что функция принимает анонимную функцию в аргументе типа (Char) -> Boolean с именем predicate. Функция predicate принимает аргумент Char и возвращает булево значение.

public inline fun CharSequence.count(
    predicate: (Char) → Boolean
): Int

Создадим собственную анонимную функцию для вывода какого-то сообщения.

println(
        {
            val year = 2019
            "Добро пожаловать в $year год"
        }()
)

Между фигурными скобками находится тело анонимной функции. После закрывающей фигурной скобкой ставится пара круглых скобок, как и положено функциям. В нашем примере используются пустые круглые скобки, но возможен вариант, когда требуются аргументы.

Анонимные функции имеют специальный функциональный тип. Переменные этого типа могут содержать анонимные функции и использоваться как обычные переменные.

Объявим переменную, которая будет содержать обычную функцию, которая в свою очередь будет иметь анонимную функцию.

val messageFunction: () -> String =
        {
            val year = 2019
            "Добро пожаловать в $year год"
        }
println(messageFunction())

Разберём синтаксис. Сначала объявляется переменная messageFunction. После имени переменной идёт двоеточие и функциональный тип. Функциональный тип объявляется в виде конструкции

() -> String (вместо String могут быть другие типы, в зависимости от задачи) и сообщает компилятору, какой тип функции содержится в переменной.

В нашем случае мы можем использовать любую функцию без аргументов (пустые скобки), которая возвращает строку.

У анонимных функции не используется ключевое слово return, однако функция неявно и автоматически возвращает результат последней инструкции в теле функции.

Пример можно написать без определения типа, полагаясь на автоматическое определение типов.

val messageFunction =
        {
            val year = 2019
            "Добро пожаловать в $year год"
        }
println(messageFunction())

Разберём случаи с аргументами. Перепишем предыдущий пример, добавив один аргумент.

val messageFunction: (String) -> String =
        {
            catName ->
            val year = 2019
            "$catName! Добро пожаловать в $year год"
        }
println(messageFunction("Васька"))

Мы указали, что анонимная функция принимает строку. Имя строкового параметра определяется внутри функции, сразу после открывающей фигурной скобки, и за ним следует стрелка.

Здесь также можно использовать автоматическое определение типов и сократить код. Но в этом случае у параметров следует указать тип.

val messageFunction =
        {
            catName: String ->
            val year = 2019
            "$catName! Добро пожаловать в $year год"
        }
println(messageFunction("Васька"))

Если функция сложная, то лучше избегать автоматического определения типов, чтобы код было читаемым.

Ключевое слово it

В анонимной функции, в которой применяется только один аргумент, вместо имени параметра можно использовать альтернативный вариант — ключевое слово

it.

Переделаем предыдущий пример. Удаляем имя параметра и стрелку в начале анонимной функции и добавляем it.

val messageFunction: (String) -> String =
        {
            val year = 2019
            "$it! Добро пожаловать в $year год"
        }
println(messageFunction("Васька"))

Ключевое слово it удобно тем, что не требует имени переменной. Но с другой стороны при сложном коде страдает читаемость кода. Нужно соблюдать баланс и использовать подобную возможность в коротких выражениях, где понимание кода не вызовет трудностей:

// другой вариант примера из начала статьи
val charNumber = "Васька". count({it -> it == 'а'})
println(charNumber) // выводит 2
// оптимизированный вариант без круглых скобок
val charNumber = "Васька".count { it -> it == 'а'}
println(charNumber) // выводит 2

Несколько аргументов

Напомню, что it можно использовать только для одного аргумента. Если анонимная функция использует несколько аргументов, то используйте именованные переменные.

val messageFunction: (String, Int) -> String =
        { catName, age ->
            val year = 2019
            "$catName! Добро пожаловать в $year год. Вам исполнилось $age лет"
        }
println(messageFunction("Васька", 5))

Выражение теперь объявляет два параметра, catName и age, и принимает два аргумента. Так как теперь больше одного параметра в выражении, ключевое слово it использовать нельзя.

Подобного рода анонимные функции называют лямбдами, и им посвящена ещё одна статья.

Дополнительное чтение

Реклама

Функции в языке программирования Erlang

    org/BreadcrumbList»>
  1. Главная
  2. Туториалы
  3. Программирование
  4. Erlang

Эрланг известен как функциональный язык программирования, поэтому вы ожидаете увидеть большой акцент на том, как работают функции в Erlang. В этой главе описывается, что можно сделать с помощью функций в Erlang.

Синтаксис объявления функции выглядит следующим образом:


FunctionName(Pattern1… PatternN) ->
Body;
  • FunctionName — имя функции — это атом.
  • Pattern1 … PatternN — Каждый аргумент является шаблоном. Число аргументов N — это арность функции. Функция уникально определяется именем модуля, именем функции и arity. То есть две функции с одним и тем же именем и в одном модуле, но с разными уровнями — две разные функции.
  • Body — состоит из последовательности выражений, разделенных запятой (,):

Следующая программа представляет собой простой пример использования функций:


-module(helloworld). 
-export([add/2,start/0]). 

add(X,Y) -> 
   Z = X+Y, 
   io:fwrite("~w~n",[Z]). 
   
start() -> 
   add(5,6).

Следует отметить следующие указатели относительно вышеуказанной программы —

  • Мы определяем две функции: один называется add, который принимает 2 параметра, а другой — функцию начала 
  • Обе функции определяются с помощью функции экспорта.  Если мы этого не сделаем, мы не сможем использовать эту функцию
  • Одна функция может быть вызвана внутри другой. Здесь мы вызываем функцию add из функции start

Анонимные функции

Анонимная функция — это функция, у которой нет имени, связанного с ней. У Erlang есть возможность определять анонимные функции. Следующая программа является примером анонимной функции.


-module(helloworld). 
-export([start/0]). 

start() -> 
   Fn = fun() -> 
      io:fwrite("Anonymous Function") end, 
   Fn().

Необходимо отметить следующие моменты в приведенном выше примере.

  • Анонимная функция определяется ключевым словом fun ()
  • Функция назначается переменной Fn.
  • Функция вызывается через имя переменной.

Функции с несколькими аргументами

Функции Erlang могут быть определены с нулевым или большим количеством параметров. Также возможна перегрузка функций, в которой вы можете определить функцию с тем же именем несколько раз, если у них есть различное количество параметров.


-module(helloworld). 
-export([add/2,add/3,start/0]). 

add(X,Y) -> 
   Z = X+Y, 
   io:fwrite("~w~n",[Z]). 
   
add(X,Y,Z) -> 
   A = X+Y+Z, 
   io:fwrite("~w~n",[A]). 
 
start() ->
   add(5,6), 
   add(5,6,6).

В приведенной выше программе мы дважды определяем функцию добавления. Но определение первой функции добавления принимает два параметра, а второе принимает три параметра.

Функции с охранными последовательностями

Функции в Erlang также могут иметь защитные последовательности. Это не что иное, как выражения, которые только при оценке на true вызовут функцию. Синтаксис функции с защитной последовательностью показан в следующей программе.


FunctionName(Pattern1… PatternN) [when GuardSeq1]->
Body;
  • FunctionName — имя функции — это атом.
  • Pattern1 … PatternN — Каждый аргумент является шаблоном.  Число аргументов N — это арность функции. Функция уникально определяется именем модуля, именем функции и arity. То есть две функции с одним и тем же именем и в одном модуле, но с разными уровнями — две разные функции.
  • Body — предложения — состоит из последовательности выражений, разделенных запятой (,).
  • GuardSeq1 — это выражение, которое оценивается при вызове функции.

Следующая программа представляет собой простой пример использования функции с защитной последовательностью.


-module(helloworld). 
-export([add/1,start/0]). 

add(X) when X>3 -> 
   io:fwrite("~w~n",[X]). 

start() -> 
   add(4).

 

Самовстраиваемые анонимные функции в C++

Дополнительные ресурсы

  • Мой последний пост о встраивании лямбда-выражений в C++.
  • Мой интерпретатор лямбда-исчисления как шаблонная метапрограмма C++.
  • Шаблоны C++: полное руководство, которое я используется для этих постов.

Короткая версия

Каждый лямбда-терм складывает свое определение в свой тип, например, лямбда. (x, x * x) имеет тип Sum < Arg, Arg > , где A — тип метапеременная x . Таким образом, везде, где применяется термин лямбда, достаточно информация для метапрограммы шаблона, чтобы развернуть тип обратно в тело функции.

Разбираем

Чтобы понять, как это работает, давайте разберем пример:

 интегрировать (лямбда (х, х * х), 0,0, 1,0, 10000) ;
 

Передача анонимных функций как типов

Прототип интегрировать функция:

шаблон <имя типа F>
статический встроенный
двойное интегрирование (F f, двойное a, двойное b, int n) ;
 

Здесь опытный программист на C++ может быть озадачен: тип параметра функции ( F ) кажется неограниченным. На практике тип, который мы передаем для этого параметра, будет кодировать все информация, необходимая для реконструкции функции, которую мы хотим вызывать. Поскольку шаблоны расширяются лениво, если мы передаем тип, который не кодировать функцию, мы получим ошибку времени компиляции, когда она попытается встроить его.

Вызов анонимных функций

Заглянув внутрь функции , интегрируем , мы увидим как это делается; вместо вызова f(x) мы видим:

 ... Встроенный::at(x) ...
 
На самом деле компилятор предупреждает, что параметр времени выполнения f даже не используется! (Это хороший показатель того, что все, что происходит, происходит во время компиляции.)

Итак, ясно, что Inline — это шаблон метапрограмма, которая реконструирует выражение, эквивалентное функция описывается F и на статических функция-член встраивает это выражение со своим аргументом вместо аргумент функции.

Построение анонимных функций

Чтобы создать анонимную функцию, нам нужно закодировать ее определение как тип. В теории компиляторов стандартно кодировать выражение в виде синтаксического дерева. Итак, нам просто нужен способ кодирования синтаксических деревьев как типов.

Сейчас мы будем считать, что все анонимные функции принимают только один аргумент (этот метод обобщается на несколько аргументов), и что он не содержит свободных переменных (этот метод также обобщается на свободный переменные).

Мы будем использовать базовый класс Exp для представления выражения с синтаксическим деревом, закодированным как тип T :

шаблон <имя типа T>
структура опыта {
  шаблон <имя типа M>
  Оператор Prod * (M m) ;
  шаблон <имя типа M>
  Оператор суммы + (M m) ;
} ;
 

Обратите внимание, как оператор вложенного члена шаблона перегружается для сложение и умножение сохраняют структуру вычисления в полученном типе. Итак, если у нас есть два выражения e1 и e2 типы которых представляют выражения времени компиляции, то тип e1 + e1 представляет выражение времени компиляции, которое вычисляет их сумму.

Для нашего очень простого демонстрационного языка мы ограничим встроенный язык предметной области, описывающий анонимные функции, только аргументами, суммы, произведения и константы. Если вы хотите сделать что-то серьезное с помощью этой техники, вы, вероятно, захотите сначала обогатить этот язык дополнительными типами выражений:

// Целочисленная константа:
шаблон 
структура I: public Exp > {
  статический I v ;
} ;
// Аргумент функции типа T:
шаблон <имя типа T>
struct Arg : public Exp< Arg > {} ;
// Сумма двух выражений:
шаблон <имя типа R1, имя типа R2>
struct Sum: public Exp > {};
// Произведение двух выражений:
шаблон <имя типа R1, имя типа R2>
struct Prod: public Exp > {};
 
Очевидно, что нам не нужно определять какие-либо функции-члены, поскольку мы никогда не используйте экземпляры этих типов. Эти типы представляют собой структуры данных времени компиляции для метапрограммы шаблона. Обратите внимание, что базовый класс этих выражений содержит их собственный тип в качестве аргумента. Именно этот метод позволяет работать перегруженным операциям в базовом классе.

Собрав все вместе, мы можем создать анонимную функцию времени компиляции, которая возводит аргумент в квадрат следующим образом:

 Арг<двойной> х ; // x теперь является метапараметром.
 ... лямбда (х, х * х) ...
  // имеет тип Prod,Arg >
 
а функция lambda — это просто синтаксическое плацебо; для анонимных функций с одним аргументом его роль эстетична:

шаблон <имя типа T, имя типа A>
статическая встроенная лямбда T (аргумент A, тело T) {
  (недействительно) аргумент ;
  (пустое) тело;
  вернуть Dummy::value ;
}
 

(Если бы вы разрешили анонимные функции с несколькими аргументами, лямбда действительно была бы необходима. )

Встраивание анонимных функций

Чтобы развернуть определение функции в точке ее использования требуется очень простая метапрограмма шаблона C++. Шаблонные метапрограммы C++ по большей части просто функциональные системы сопоставления с образцом и переписывания терминов.

Предполагая, что каждая анонимная функция времени компиляции F вызывается как Inline::at(x) , следующая метапрограмма шаблона встраивает функцию, закодированную в типе F , а затем применяет ее к x ;

шаблон <имя типа E>
структура Встроенный {
} ;
// Встроенные константы:
шаблон <инт я>
структура Inline< I > {
  статический встроенный int at (int arg) {
    (пусто)аргумент ;
    вернуть я;
  }
} ;
// Встроенные аргументы:
шаблон <название типа X1>
структура Inline > {
  статический встроенный X1 в (X1 arg) {
    вернуть аргумент ;
  }
} ;
// Встроенные суммы:
шаблон < имя типа E1, имя типа E2 >
структура Inline > {
  шаблон <имя типа А>
  статический встроенный A at (A arg) {
    вернуть Inline::at(arg) +
           Встроенный::at(arg) ;
  }
} ;
// Встроенные продукты:
шаблон < имя типа E1, имя типа E2 >
структура Inline > {
  шаблон <имя типа А>
  статический встроенный A at (A arg) {
    вернуть Inline::at(arg) *
           Встроенный::at(arg) ;
  }
} ;
 

Заметки составителя

Ключевым предположением в этой демонстрации является то, что компилятор C++ будет подчиняться ключевому слову inline . Конечно, компиляторы C++ не обязаны этого делать, поэтому важно понимать, когда они это сделают. В моих собственных тестах g++ начал встраивать функции с пометкой static. inline на уровнях оптимизации -O2 и выше.

Глядя на код x86, сгенерированный для

  двойная площадь = интегрировать (лямбда (х, х * х), 0,0, 1,0, 10000) ;
  printf("интегрировать: %f\n", площадь) ;
 
в -O2 я нашел это внутри main :

L11:
        мовапад %xmm1, %xmm0
        mulsd %xmm1, %xmm0
        mulsd %xmm2, %xmm0
        добавляетd %xmm0, %xmm3
        включая %eax
        добавляетd %xmm2, %xmm1
        cmpl $10000, %eax
        jne L11
        мовапад %xmm3, %xmm0
        утечка LC2(%rip), %rdi
        movl $1, %eax
        вызов _printf
 
Как видите, единственный вызов — printf , а цикл встроен в основной .

Код

Загрузите полный демо-код.

В качестве упражнения вы можете расширить его с помощью более богатого набора выражений в DSEL. Более захватывающим обновлением было бы объединение значений free переменные через значение времени выполнения анонимной функции; в в настоящее время это просто фиктивное значение, которое оптимизатор устраняет. Тогда у вас будет самовстраивающееся анонимное замыкание !


Лямбда-функции и оболочки в C++

Обзор

Лямбда-выражения являются встроенными анонимными функциями, т. е. у них нет имени. Lambda состоит из различных частей, таких как предложение захвата, параметры, ключевые слова, тело, исключения и т. д. Существует три способа захвата внешних переменных в наше лямбда-выражение. Лямбда и функторы очень похожи друг на друга. Лямбды — это компактные версии функторов. Лямбды очень полезны при написании алгоритмов STL.

Лямбда-функция в C++

Лямбда-функции в C++ — это встроенные функции, которые можно писать для кратких фрагментов кода, не предназначенных для повторного использования, поэтому им также не нужно давать имена. Он был представлен в Modern C++, начиная с C++11. Написание лямбда-выражения вместо функции везде, где это необходимо, делает код компактным, чистым и простым для понимания.

Мы можем определить лямбда-выражение локально там, где мы хотим передать его функции в качестве аргумента или там, где нам требуется его вызвать. Это анонимные функциональные объекты, которые широко используются для написания встроенных функций.

Давайте посмотрим, как мы можем создать и использовать лямбда-выражение

Синтаксис

Лямбда-выражение состоит из предложения захвата, параметров, возвращаемого типа и тела метода.

  • пункт захвата — это список переменных, которые должны быть скопированы внутри лямбда-функции в C++. Мы также можем использовать его для инициализации переменных.
  • параметры — ноль, один или более аргументов для передачи лямбде во время выполнения.
  • mutable — mutable — необязательное ключевое слово. Это позволяет нам изменять значение переменных, которые фиксируются вызовом по значению при записи в лямбда-выражении.
  • возвращаемый тип — это необязательный параметр, так как его оценивает компилятор. Однако в некоторых сложных случаях компилятор не может определить тип возвращаемого значения, и поэтому нам нужно его указать.
  • тело метода — То же, что и обычное определение метода. Здесь записываются все операции, которые необходимо выполнить при вызове лямбда-выражения.

Давайте посмотрим на пример лямбда-выражения.

Пример:

Вот пример лямбда-функции в C++ для понимания синтаксиса и использования. Возьмем список чисел больше 1 и отделим простые числа от списка. Также мы добавим их в другой список с помощью лямбда-выражения.

Вывод:

В приведенном выше примере мы посещаем каждый элемент вектора чисел. v1 — это список, в котором мы собираемся хранить простые числа. Мы захватываем (чтобы мы могли использовать его в нашей лямбда-функции) v1 по ссылке, а ключевое слово mutable позволяет нам редактировать его в соответствии с требованиями программы. Если число простое, оно добавляется к v1 и, наконец, мы печатаем компоненты списка.

Способы захвата внешних переменных из вложенной области

Лямбда-функция в C++ более мощная, чем обычная функция. Обычные функции могут использовать только глобальные переменные или переменные, переданные функции. В то время как лямбда-выражения могут использовать локальные переменные, присутствующие в основном методе, а также параметры, передаваемые в лямбда-выражение вместе с глобальными переменными. Чтобы использовать переменные, отличные от параметров, мы используем предложение захвата.

Существует три способа захвата этих внешних переменных из объемлющей области:

Захват по ссылке

Внешние переменные могут быть захвачены с использованием их ссылки на лямбда-выражения. Мы передаем адрес переменной в предложение захвата соответствующего выражения. Это будет ссылаться на исходную переменную, и поэтому изменения также отразятся на исходной.

Синтаксис:

Здесь мы передаем ссылку на две переменные num1 и num2, используя символ &.

Захват по значению

Мы также можем передавать значения внешних переменных в лямбда-выражение. Здесь мы просто передаем имя переменной в предложение захвата. Захват по значению означает, что мы эффективно копируем значение переменной в новую переменную, которая находится внутри лямбда-выражения. На исходную переменную лямбда-функция в C++ не влияет.

Синтаксис:

Сумма должна быть инициализирована перед использованием в предложении захвата. Мы передаем значение суммы в лямбда-выражение.

Захват обоими (смешанный захват)

В предложении захвата можно передать более одной переменной. Кроме того, не все эти переменные нужно передавать как значения или по ссылке. Таким образом, мы можем передать как комбинацию значений, так и ссылку.

Синтаксис:

В приведенном выше коде мы передаем идентификатор по ссылке и имя по значению. В предложении захвата может быть несколько переменных, и они могут быть ссылками и/или значениями.

Пример:

Ввод:

Вывод:

В приведенном выше примере мы передаем userpw по значению и пароль по ссылке. Он возвращает Login Successful, поскольку пароль совпадает с введенным пользователем. checkPasscode — это имя, присвоенное лямбда-выражению. Он не сохраняет значения, так как наше лямбда-выражение ничего не возвращает.

Синтаксис, используемый для захвата всех переменных

Чтобы иметь возможность захватить все переменные и использовать их в нашем лямбда-выражении, мы используем & и =.

  • [&] — Используется для захвата всех переменных по ссылке. Здесь мы передаем адрес переменной. Таким образом, если мы вносим какие-либо изменения в переменную в лямбда-выражении, это отражается в фактической переданной переменной.
  • [=] — Используется для захвата всех переменных по значению. Изменения, внесенные в эти значения, не отражаются в фактической переменной. Здесь мы только передаем значение переменной. Исходная переменная никак не затрагивается.

Примечание: когда предложение захвата пусто, то есть [], тогда он может получить доступ только к локальным для него переменным.

Плюсы и минусы захвата по ссылке

Когда мы захватываем переменные по ссылке, используется адрес исходной переменной, и, таким образом, все модификации выполняются в самом значении переменной. Принимая во внимание, что когда мы передаем значение, создается еще одна копия переменной, и в ней происходят изменения. Кроме того, если функция возвращает лямбда-функцию, мы не должны использовать захват по ссылке, поскольку ссылка будет недействительной.

Подробнее о новом синтаксисе Lambda

Возвращаемые значения

Тип возвращаемого значения указан после стрелки ->. Если он не указан, компилятор оценивает самостоятельно. В случае сложных программ мы обязаны указать это. Возвращаемое значение должно быть возвращено внутри лямбда-функции, если оно не пусто.

Спецификации бросков

Спецификации бросков (также называемые исключениями) необязательно упоминать в лямбда-функции в C++. Мы можем указать, какие исключения выдает наше лямбда-выражение. Поскольку мы передаем это другой функции в качестве аргумента, эта функция ожидает, что лямбда выдаст только определенный набор исключений. Пишется после ключевого слова mutable и после параметров при отсутствии ключевого слова mutable.

Ключевое слово Auto

Ключевое слово Auto в C++ позволяет нам присвоить имя лямбда-функции. Мы можем вызвать эту лямбда-функцию, используя ее имя, точно так же, как мы вызываем переменные по их именам. Мы также можем использовать ключевое слово auto для передачи аргументов лямбда-выражению. Это позволяет нам выполнять определенные операции с несколькими типами данных. Его также называют общей лямбдой. Мы используем ключевое слово auto вместо типа данных.

Например:

Выход:

В приведенном выше примере add является лямбда-выражением. auto перед добавлением дает имя лямбда-выражению. Таким образом, используя это имя, мы можем вызывать его несколько раз. auto перед параметрами a и b позволяет нам передавать данные разных типов. Компилятор выводит тип данных на основе предоставленных данных.

Инициализировать переменные-члены и изменяемое ключевое слово

В предложении захвата мы можем создавать или инициализировать переменные. Кроме того, мы можем захватывать существующие внешние переменные. Чтобы иметь возможность изменять любую переменную, которая передается в лямбда-выражение с помощью предложения захвата, мы должны написать ключевое слово mutable после списка параметров в скобках (). Это необязательно, поэтому, когда предложение захвата пусто или мы не хотим изменять переменные предложения захвата, мы можем пропустить запись mutable в лямбда-выражении.

Давайте рассмотрим пример , чтобы лучше понять концепцию.

Выходные данные:

В приведенном выше примере переменная значения создается и инициализируется в предложении захвата. Его значение инициализировано как 10. Мы добавляем значение к x и меняем значение на 18. Это возможно, поскольку мы используем ключевое слово mutable после параметров в лямбда-выражении, иначе оно выдает исключение. Наконец, мы печатаем значения переменной value и x.

Немедленный вызов C++ Lambda (IIFE)

Так как лямбда-выражения являются анонимными выражениями, которые следует использовать и забыть. Это хороший способ прямого выполнения кода без заполнения глобального пространства имен, если код не зависит ни от каких условий.

Вывод:

В приведенном выше примере у нас есть сведения об ученике, которые присутствуют в разных переменных. Его необходимо сгруппировать по формату и распечатать. Лямбда-функция объявляется, определяется и вызывается с помощью (), сразу после } (закрывающая фигурная скобка). Поскольку функция предназначена только для выполнения, она вызывается непосредственно после определения. Это также известно как немедленный вызов лямбды в C++.

Примечание: [=] в приведенном выше коде захватывает все внешние переменные по значению.

Лямбда-выражения против функторов

По сути, функторы — это не функции, это функциональные объекты. Объекты класса в C++, который определяет метод operator() (также называемый оператором вызова или оператором приложения), называются функторами. Объекты этого класса выглядят как функция, так как заканчиваются круглыми скобками (). Функтор имеет преимущество перед обычными функциями, поскольку он может поддерживать несколько независимых состояний, по одному для каждого экземпляра функтора, в то время как функции поддерживают только одно состояние.

Лямбды — это анонимные функциональные объекты, которые широко используются для написания функций на месте. Они не используются повторно, а также не называются.

Давайте рассмотрим пример, чтобы понять сходство и различие между ними. Мы возьмем функтор для кодирования заданного целочисленного значения и создадим лямбда-функцию для декодирования закодированного значения.

Вывод:

В приведенном выше примере MyFunctor является функтором. Это класс, который расширяет функцию operator() и принимает x в качестве параметра. Он выполняет над ним некоторые операции для кодирования значения и возвращает его.

Чтобы декодировать закодированное значение в x, мы используем переменную y для сохранения результата. Мы используем лямбда-выражение для вычисления исходного значения.

И функтор, и лямбда-выражения очень похожи друг на друга. Единственная разница в том, что лямбда не имеет имени, а ее код представлен в упрощенном виде. Мы можем использовать функторы в нескольких местах, а также хранить состояние, тогда как лямбды нельзя использовать более одного раза.

Функторы являются объектами классов, поэтому они немного сложнее лямбда-выражений. Кроме того, их реализация лежит в классе и используется в методе main(), тогда как лямбда-выражение реализовано в самом методе main().

Lambda и STL

Алгоритмы STL работают с элементами контейнера, такими как векторы, массивы, стеки и т. д., используя функциональные объекты, эти функциональные объекты передаются в качестве аргумента алгоритмам.

Например:

Вывод:

Приведенный выше код печатает цвета, длина которых больше или равна 5.

В приведенном выше примере мы передаем объект анонимной функции с реализацией непосредственно в функцию STL. Без лямбда-выражения нам придется создать функциональный класс, объект этого функционального класса, а затем мы сможем передать однострочную функцию в STL. Скорее, лямбда не только делает код компактным, но и снижает сложность программы при той же функциональности.

Применение лямбда-выражений с соответствующим примером

Применение лямбда-выражения:

  1. Лямбда-выражения легковесны, удобочитаемы и компактны.
  2. Лямбды улучшают локальность кода, функторы пишутся далеко от того места, где они вызываются.
  3. Лямбда-выражения допускают универсальную реализацию функции, т. е. мы можем определить общую функцию и использовать ее для нескольких типов данных, таких как int, float, double и т. д.
  4. Используя лямбда, мы можем несколько раз перегружать одну и ту же функцию в самом основном методе.
  5. Лямбда-выражения хороши для хранения временного состояния переменной.

Возьмем для примера список сотрудников, проверим количество отработанных ими часов в неделю. Если они работали более 68 часов, то мы добавим поощрения в размере 20 процентов от их заработной платы.

Выходные данные:

  • сотрудник представляет собой массив, в первом столбце хранится идентификатор сотрудника, во втором столбце хранится количество отработанных часов, а в третьем столбце хранится начальная зарплата.
  • С помощью лямбда-выражения мы проверим количество отработанных часов каждым сотрудником и добавим поощрения, если рабочее время превышает 68 часов в неделю. Мы захватываем сотрудника по ссылке, лямбда-выражение не принимает никаких параметров и не возвращает никакого значения. Он вносит изменения в таблицу сотрудников, поэтому используется ключевое слово mutable.

Заключение

  • Лямбда-выражения — это краткие фрагменты кода, которые нельзя использовать повторно и которые действуют как анонимная функция.
  • Лямбда-выражение имеет различные компоненты, такие как предложение захвата, параметры, изменяемые и автоматические ключевые слова, исключения, тип возвращаемого значения и т. д.
  • Ключевое слово auto позволяет нам присвоить имя лямбда-выражению, а также создать общее лямбда-выражение.
  • В предложении захвата можно выполнять создание и инициализацию переменных. Для редактирования этих переменных мы используем ключевое слово mutable.
Оставить комментарий

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *