Re: Лямбда и замыкания для C++
От: x-code  
Дата: 10.08.10 20:24
Оценка: 25 (9)
Здравствуйте, vladimir_i, Вы писали:

_>Кто-нибудь может объяснить в чем же там суть с точки зрения C/C++?

_>Интересен даже гипотетический вариант кода, пусть даже невалидный, но объясняющий сабж.

Лямбды — это функции, объявленные прямо в выражениях. Например, с таким синтаксисом
[ccode]
int sum = func(100,200,(x,y)=>x+y);
int mul = func(100,200,(x,y)=>x*y);
[/code]

Здесь выражения вида (x,y)=>x+y как раз и есть лямбда-функции. Разумеется, могут быть синтаксические нюансы — например, поскольку С++ типизирован, то может потребоваться явное указание типа. Вот, проследите за усложнением синтаксиса — от лямбды до обычного объявления функции:
 (x,y) => x+y;
 (int x, int y) => x+y
 lambda(int x, int y) { return x+y; }      // ключевое слово lambda
 int my_lambda_func(int x, int y) { return x+y; } // обычная функция Си

В C++0x, кстати, придумали другой синтаксис, с квадратными скобками.
В функцию func передается нечто вроде указателя на лямбду. Функция может при необходимости вызвать ее.

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

void outer_func()
{
  int x = 100;
  int inner_func()
  {
     return x + 10;   // использование локальной переменной
  }
}

Это бы вполне работало даже в чистом Си, если бы не возможость вернуть функцию наружу (через указатель на функцию, к примеру). Предположим, что outer_func возвращает указатель на свою вложенную функцию inner_func. Понятно, что эту возможность можно просто запретить. Или пойти тем путем как в Си — возврат адреса локальной переменной бессмысленен и никто так не делает. Но можно пойти до конца и реализовать функции как первоклассные объекты — т.е. чтобы с функциями можно было делать что угодо, в т.ч. возвращать из других функций.

Для этого нужно вместо возврата указателей на функции ввести понятие "делегаты" — специальные объекты, включающие в себя функцию и блок данных, необходимый для работы этой функции. Тогда, если бы oiter_func возвращала свою вложенную функцию, она могла бы возвращать делегат, содержащий, например, указатель на вложенную функцию и поле данных — переменную объемлющей функции. Вот псеводкод, который мог бы получиться при декомпиляции фрагмента с замыканием:
struct DELEGATE
{
  int (*fptr)();  // функция (хотя укзатель на функцию избыточен - достаточно просто объявить класс с функцией-членом)
  int x;          // блок данных; в нашем случае - переменная, "замкнутая" на функцию
};
DELEGATE outer_func()
{
  DELEGATE d;
  d.x = 100;
  int inner_func()
  {
     return d.x + 10;   // использование внешней локальной переменной
  }
  d.fptr = &inner_func;
  return d; // возвращаем функцию - ВМЕСТЕ и указатель, и внешняя локальная переменная! 
}

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

Все это, как видно, можно реализовать на С++ и в явном виде, причем без указателей на функции
struct DELEGATE // вложенная функция и переменная вынесены в отдельный класс
{
  int x;
  int inner_func()
  { 
    return x + 10;
  }
};
DELEGATE outer_func()
{ 
  DELEGATE d;
  d.x = 100;   
  return d;
}


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