Я старый C++ программист.
У меня не было хорошей возможности опробовать один тех языков, на которых в wiki объясняется практическое применение лямбд и замыканий.
Кто-нибудь может объяснить в чем же там суть с точки зрения C/C++?
Интересен даже гипотетический вариант кода, пусть даже невалидный, но объясняющий сабж.
Здравствуйте, vladimir_i, Вы писали:
_>Я старый C++ программист. _>У меня не было хорошей возможности опробовать один тех языков, на которых в wiki объясняется практическое применение лямбд и замыканий.
_>Кто-нибудь может объяснить в чем же там суть с точки зрения C/C++? _>Интересен даже гипотетический вариант кода, пусть даже невалидный, но объясняющий сабж.
Лямбды это очень просто. Это возможность всунуть функцию куда угодно. Типа,
Здравствуйте, vladimir_i, Вы писали:
_>Кто-нибудь может объяснить в чем же там суть с точки зрения C/C++? _>Интересен даже гипотетический вариант кода, пусть даже невалидный, но объясняющий сабж.
Здравствуйте, vladimir_i, Вы писали:
_>Я старый C++ программист. _>У меня не было хорошей возможности опробовать один тех языков, на которых в wiki объясняется практическое применение лямбд и замыканий.
_>Кто-нибудь может объяснить в чем же там суть с точки зрения C/C++? _>Интересен даже гипотетический вариант кода, пусть даже невалидный, но объясняющий сабж.
_>Спсибо!
в boost::lambda это выглядит так:
list<MClass*> m_cls;
...
for_each(m_cls.begin(), m_cls.end(), bind(_1->*&MClass::foo,"hello"));
или
remove_if(m_cls.begin(), m_cls.end(), _1->*&MClass::is_Working == constant(true));
и т.п. там есть и условный выбор и втраиваемые циклы, но имхо проще для этого функтор написать(тестировать и поддерживать проще)
Здравствуйте, 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;
}
Ясно что замыкания нужны как раз для лямбда-функций — поскольку лямбды кототкие и заточены для выполнения конкретной работы в конкретном месте кода, они вполне могут ссылаться на разные локальные переменные.
Здравствуйте, vladimir_i, Вы писали:
_>Я старый C++ программист. _>У меня не было хорошей возможности опробовать один тех языков, на которых в wiki объясняется практическое применение лямбд и замыканий.
_>Кто-нибудь может объяснить в чем же там суть с точки зрения C/C++? _>Интересен даже гипотетический вариант кода, пусть даже невалидный, но объясняющий сабж.
_>Спсибо!
Здравствуйте, x-code, Вы писали:
XC>Для этого нужно вместо возврата указателей на функции ввести понятие "делегаты" — специальные объекты, включающие в себя функцию и блок данных, необходимый для работы этой функции.
Терминологическая путаница. "Делегаты" — это функциональный объект, вызывающий другой код. Просто такой паттерн в ОО-системе, прямой наследник ОО-понятия "интерфейс" (не тот который interface в Java/C#, а который классический). Делегат необязательно бывает "лямбдой" с т.з. ФП.
В общем, "функциональный объект" вместе с необходимыми данными — это просто "функциональный объект", таков, какой он есть по своей природе. Через систему типов не функциональных языков описывается как ф-ия + необходимые данные к ней.
В случае же, когда такой функциональный объект генерируется автоматически через поддержку синтаксиса языка и использует видимые коду данные вне тела ф-ии, — это есть "замыкание". Иными словами, "замыкание" — это не синоним "функционального объекта", а обозначение действия, его создающего, и далее, по устоявшемуся принципу, этим термином называют ф-е объекты, созданные таким образом, они же "лямбды".
XC>[code] XC>struct DELEGATE XC>{ XC> int (*fptr)(); // функция (хотя укзатель на функцию избыточен — достаточно просто объявить класс с функцией-членом) XC> int x; // блок данных; в нашем случае — переменная, "замкнутая" на функцию XC>};
Это пример описание обычного функционального объекта в рамках языка С/С++, никакого не делегата, и далее у тебя его использование по декомпилированному коду тоже. Только функциональной чистоты ради переменную x надо было инициализировать в момент конструирования функционального объекта и объявить константной. В том то и фишка, что декомпилированный код, разумеется, будет содержать лишь "обычные функциональные объекты", коими умеет оперировать исполняющая среда, а "замыкание" — это не более чем синтаксическая помощь компилятора по определению таких ф-ных объектов по-месту.
XC>Ясно что замыкания нужны как раз для лямбда-функций — поскольку лямбды кототкие и заточены для выполнения конкретной работы в конкретном месте кода, они вполне могут ссылаться на разные локальные переменные.
Неважно, короткие лямбды или нет, для этого термина важен способ получения этих ф-ных объектов.