quarta-feira, 27 de maio de 2009

Funções e expressões Lambda no C++ 0x


Um dos recursos mais importantes do próximo padrão C++ que ainda esta em desenvolvimento é a chamada função lambda.Expressões lambdas definem e constroem implicitamente funções objetos sem nome que na prática se comportam como as já conhecidas funções objetos. Como um trecho de código vale mais do que mil palavras, no Código 1 abaixo pode-se ver a utilização básica de uma função lambda.


Código 1

int main()
{
vector v;

for (int i = 0; i < 10; ++i)
v.push_back(i);

for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

cout << endl;
}



O [] é chamado introdutor lambda (lambda-introducer) e informa o início de uma função lambda. A declaração do parâmetro lambda é representado por int n e o corpo da função lambda é { cout << n << “ “;. Por default uma função lambda retorna void. Segue abaixo no Código 2 como seria este mesmo código escrito no padrão atual do C++ usando o tradicional funtor.


Código 2


struct Functor {
void operator()(int n) const
{
cout << n << " ";
}

};

int main()
{
vector v;
for (int i = 0; i < 10; ++i)
v.push_back(i);

for_each(v.begin(), v.end(), Functor());

cout << endl;
}


Alguns de vocês já devem estar se perguntando se é permitido colocar vários comandos dentro de uma função lambda. A resposta é sim e um exemplo é mostrado no Código 3:


Código 3


int main()
{
vector v;

for (int i = 0; i < 10; ++i)
v.push_back(i);

for_each(v.begin(), v.end(), [](int n) {
cout << n;
if (n % 2 == 0)
cout << " even ";
else
cout << " odd ";
});

cout << endl;
}



Ao avaliar o Código 3 você já pode estar imaginando a “macarronada” que um programador pode fazer no código ao usar expressões lambda. Se já era fácil fazer códigos ilegíveis agora ficará ainda mais simples. Acredito que em breve vão lançar o Obfuscated C++ 0x Code Contest. Pode ser que criem coisas mais assustadoras do que o já conhecido International Obfuscated C Code Contest (http://www.ioccc.org/).

Antes que você me pergunte, expressões lambdas podem retornar qualquer tipo de dados. Se o corpo de sua função é do tipo { return expr; }, o tipo de retorno será automaticamente deduzido para o tipo de expr. No Código 4 é mostrado um exemplo:


Código 4


int main()
{
vector v;

for (int i = 0; i < 10; ++i)
v.push_back(i);

deque d;

transform(v.begin(), v.end(), front_inserter(d), [](int n) { return n * n * n; });

for_each(d.begin(), d.end(), [](int n) { cout << n << " "; });

cout << endl;
}



No Código 4 o tipo de n*n*n é int e por isso a função lambda retorna int. Lambdas com expressões mais complexas não são capazes de deduzir o tipo de retorno e por isso o programador deve informar explicitamente. O Código 5 ilustra como isso é feito:

Código 5

int main()
{
vector v;

for (int i = 0; i < 10; ++i)
v.push_back(i);

deque d;

transform(v.begin(), v.end(), front_inserter(d), [](int n) -> double {
if (n % 2 == 0)
return n * n * n;
else
return n / 2.0;
});

for_each(d.begin(), d.end(), [](double x) { cout << x << " "; });

cout << endl;
}


O trecho “>- double” é a cláusula opcional de tipo de retorno lambda. O motivo da cláusula não ficar mais a esquerda como acontece com todos os tipos de funções C é que [ ] deve ficar antes para informar ao compilador o início da função lambda. Se o programador não escrever a clausula de tipo de retorno, o compilador provavelmente vai gerar um warning para cada return.

Hoje termino a primeira parte do estudo de expressões lambda. No próximo post irei mostrar expressões lambdas que contem membros de dados.

Referência


O artigo original fonte de inspiração deste post e origem dos código de exemplo pertence ao blog do Visual C++.


[]’s


Leonardo X. T. Cardoso
blog comments powered by Disqus