Wednesday, May 13, 2009

О замыканиях в Python и С++

Предыдущий пост вызвал множество споров в pythonua@c.j.r на тему сложности синтаксиса С++ и сложности синтаксиса лямбд в частности. Действительно, в С++ 0x лямбды - это не one-line expressions, а полноценные анонимные функции. Именно поэтому синтаксис и так сложен.
Например, возьмём Python и его лямбды. Так как синтаксис Python изначально минималистичен, то и лямбды в нём выглядят аккуратно и понятно. Однако, с другой стороны такая минималистичность может сыграть и злую шутку. Вот пример - не так давно поднимался вопрос о замыканиях в Python. Речь в этой статье шла о таком небольшом сниппете:

l = []
for i in range(2):
for j in range(2):
l.append(lambda: i + j)
При такой реализации замыкания не происходит - в l мы получим абсолютно одинаковые функции. Основной вывод вышеприведенной статьи - правильная реализация замыканий в Python - это:
def lsum(n, m):
return lambda: n + m

l = []
for i in range(2):
for j in range(2):
l.append(lsum(i, j))
Но что мы видим в этом коде? Вместо удобного inline использования - отдельная функция, которая отвлекает внимание и всё портит.
Теперь вернёмся к С++ и посмотрим как в игру вступает один из синтаксических наворотов - capture list:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/function.hpp>

int main()
{
std::vector<boost::function <int()>> l;
for (int i=0;i<2;++i)
for (int j=0;j<2;++j)
l.push_back([i,j]{ return i + j; });
for_each(l.begin(),
l.end(),
[](boost::function <int()> f){ std::cout << f() << "\n"; });
}
Данная программа выводит:
0
1
1
2
Как и ожидалось. Таким образом, "синтаксический мусор" в С++ 0x - это по большей части нужные и полезные вещи. Спасибо за внимание!

C++ 0x уже сегодня?

Вступление

С++ 0x - кодовое имя грядущего стандарта С++, который несет в себе огромное количество изменений. Если и до этого язык С++ был одним из самых нагруженных и сложных, то с приходом нового стандарта он может вообще быть погребен под собственной массой. Но случится ли это? Ответу на этот вопрос и посвящен данный пост.

Основная часть

На BoostCon было точно подмечено, что "С++ 0x" нужно читать как "C++ 0A","C++0B", etc, однако многое можно попробовать уже сейчас.
Для этого я собрал gcc из cxx0x-lambdas-branch, который, по-моему, сейчас максимально близок к C++0x (бранч периодически мержится с основной веткой, но в этом бранче полностью отстутствуют Concepts). О том, как собирать - смотрите ссылки ниже.
Итак, имеем:
$ bin/gcc --version
gcc (GCC) 4.5.0 20090421 (experimental)
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Попробуем что-нибудь написать:
#include <iostream>
#include <vector>
#include <algorithm>


int main()
{
std::vector<int> v = {1,2,3,4,5};
std::vector<std::pair<int,int>> v2;
std::transform(v.begin(),
v.end(),
std::back_inserter(v2),
[](int x){return std::pair<int,int>(x, x*x);});
for_each(v2.begin(),v2.end(),[](std::pair<int,int> x){std::cout<<"("<<x.first<<","<<x.second<<")";});
int s = 0;
for_each(v.begin(),v.end(),[&s](int x){s+=x;});
std::cout<<"\nResult:"<<s<<"\n";
// static_assert(false, "Dummy assertion!");
auto is_42 = [](int x) {return x == 42;};
auto answer = 42;
if (is_42(answer))
{
std::cout<<"Yes, it is!\n";
}
decltype(answer + 42) new_answer = 43;//?
std::cout<<"New Answer:"<<new_answer<<"\n";
}
Впечатляет? Присмотритесь внимательней, в этом примере: initializer lists(список инициализации для v), right angle brackets (две правые угловые скобки в определении v2), lambda(с захватом переменных и без), static_assert (закомментирован), auto (для простых случаев и для лямбда-выражений), decltype(для определения типов). Меня очень впечатлило. Несмотря на то, что в язык добавлены новые элементы, язык стал только выразительней, а никак не сложнее.
Таким образом, С++ вбирает в себя всё новые и новые идеи. Несложно заметить, что С++ 0x - огромный шаг поближе к Haskell. Кто считает, что это чепуха - присмотритесь к Variadic Templates:
template<unsigned... Args> struct mysum;
template<>
struct mysum<> {
static const int value = 0;
};

template<unsigned x, unsigned... Args>
struct mysum<x, Args...> {
static const int value = x + mysum<Args...>::value;
};

int main()
{
static_assert(mysum<10,20,11,1>::value==42, "Wow!");
}
Variadic Templates уже доступны, однако работают не полностью.

Выводы

С++ 0x - попытка скачкообразно осовременить С++. И, как мне кажется, эта попытка вполне может оказаться удачной.

Ссылки

Спасибо за внимание!

Tuesday, May 12, 2009