Tuesday, September 02, 2008 3:02 PM

A série de artigos sobre high-order functions e Linq gerou algumas conversas sobre as lambda expressions que utilizei. Ali, de bate pronto, tive dificuldades de explicar o conceito e percebi que ele não é tão trivial tanto para explicar, como para entender.

Então vou tentar apresentar de uma forma alternativa, mostrando a evolução da linguagem em direção às lambdas expressions.

No .Net framework 1.1 os delegates já existiam. Sua utilização prática, no entanto, era restrita aos eventos. Não que isto fosse uma restrição do framework, era mais uma questão de pouco conhecimento dos desenvolvedores mesmo.

Como expliquei no Brincando com C#, os delegates permitem que uma função seja passada como parâmetro ou retorno de outra função. Desta forma posso delegar parte do meu algoritmo para outra função.

Por exemplo, se tenho um método que transforma linhas de texto. Por algum motivo a regra de transformação pode variar. Para não precisar reescrever o método inteiro para cada situação, utilizo um delegate para isolar este trecho de código e poder reaproveitar todo o resto.

O passo-a-passo que a gente já conhece: Declara um delegate, cria um método que recebe o delegate como parâmetro, cria o método que “implementa” o delegate.

    public delegate string Parser(string line);

    public IEnumerable<string> ParseText(IEnumerable<string> text, Parser parser)
    {
        var lines = new List<string>();

        foreach (string line in text)
        {
           lines.Add(parser(line));
        }

        return lines;
    }

    private string RemoveSpaces(string line)
    {
        return line.Replace(" ", "");
    }

Utilização é simples, chama o Parser, mandando como parâmetro o RemoveSpaces.

    var parsedLines =  (List<string>) ParseText(lines, RemoveSpaces);

Anonymous methods

O .Net Framework 2.0 trouxe a novidade dos anonymous methods. O nome não podia ser mais específico, métodos anônimos são ... anônimos! Ou seja, sem nome. Não é necessário definir formalmente o método, posso simplesmente declará-lo localmente.

    var parsedLines = (List<string>) ParseText(lines, 
                                               delegate(string line)
                                               {
                                                   return line.Replace(" ", "");
                                               });

Sinceramente, para um método que deveria ser anônimo, ele é um método bem declarado e tem uma sintaxe verborrágica.

Vamos fazer um exercício para imaginar uma sintaxe um pouco mais simples, retirando tudo o que desnecessário nesta declaração. Na Figura marquei o que é inútil nesta declaração. A começar por definir o método como um delegate, já que a chamada do método espera um delegate isto é totalmente desnecessário. Tipificar o(s) parâmetro(s) também é desnecessário, pois a assinatura do método já foi declarada no delegate. Por último, o parêntesis que determina o início e o fim do método.

anonMethod

 

 

 

 

Vemos que seria possível definir uma sintaxe ainda mais simples do que os métodos anônimos. E é exatamente isto que foi feito para o .Net Framework 3.0, com as Lambdas expressions.

 

Lambda expressions

O .Net Framework 3.0 trouxe esta nova sintaxe para escrever métodos anônimos, mais concisa e mais legível. O exemplo canônico, e mais simples, das expressões lambdas, é o calculo de uma raiz quadrada:

lambda

 

 

A Figura mostra que à esquerda são declarados os parâmetros da expressão, à direita a expressão em si. Neste simples exemplo é definido o parâmetro de entrada x e a expressão é a multiplicação do parâmetro por ele mesmo, ou seja, a sua raiz quadrada.

    var parsedLines = (List<string>) ParseText( lines, line => line.Replace( " ", "" ) );

Nosso exemplo, utilizando uma expressão lambda no lugar do método anônimo, fica bem mais simples e limpo. Uma coisa que pode confundir é como o compilador sabe qual o tipo do parâmetro line? Elementar, a função espera um delegate, cuja assinatura recebe um parâmetro do tipo string, por inferência, o compilador percebe que o parâmetro é do tipo string. A IDE utiliza a mesma regra para oferecer o intellisense, como mostra a Figura.

intellisense

 

 

 

 

 

 

A sintaxe lambda é bem mais robusta e complexa do que mostram estes dois exemplos que fiz aqui. Ela aceita parâmetros com tipo definido, métodos sem parâmetros, entre outras coisas. Um guia mais completo da sintaxe pode ser encontrado aqui.

Espero ter ajudado um pouco na compreensão deste novo conceito do .Net Framework.

< Exemplos >

Comments

At 9/6/2008 1:21 AM, Tiago Soczek said:

# re: Introdução às expressões Lambda

Parabéns pelo artigo, ficou bem fácil de entender.

Tenho trabalhado com C# 3.0 diariamente e não sinto saudades nenhuma do 2.0 (não que seja ruim) mas essas "novas" features são uma mão na roda!

Dois projetos que vi o uso das lambda expressions (+ linq) de uma forma bem interessante foram:

http://code.google.com/p/fluent-nhibernate/
http://code.google.com/p/moq/

Vale a pena dar uma olhada no source.

[]'s
At 9/6/2008 7:38 PM, Eduardo Miranda said:

# re: Introdução às expressões Lambda

Tiago,

Muito obrigado pela contribuição, uma das melhores formas de aprender, e evoluir, é ler código-fonte.

Achei muito interessante também a aplicação destes conceitos na nova versão do Rhino Mocks, apesar de não olhado o código-fonte, a API ficou muito boa.
Post Comment
Title *
Name *
Email (never displayed)
Website
Comment * (Allowed tags: blockquote, a, strong, em, p, u, strike, super, sub, code)  
Please add 5 and 6 and type the answer here: