Elemar DEV

Tecnologia e desenvolvimento

Functional Programming–Outras considerações

Extension Methods, métodos e tipos anônimos, lambda expressions, dentre outras tecnologias permitem que escrevamos códigos menores, mais declarativos e mais assertivos.

LINQ é expressão de todas essas melhorias, mas, ao meu ver, é apenas um demonstrativo das melhorias que podemos fazer em nossos códigos.

É certo que linguagens mais novas – como F#, IronPython ou IronRuby – tem uma sintaxe mais fácil e direta para abordagem funcional. Mas, de forma alguma, não podemos dizer que estamos mal servidos usando C#.

Nesse post, pretendo explorar algumas “coisinhas” interessantes que conseguimos fazer em C#. Aplicações práticas do paradigma de programação funcional no dia-a-dia de nossos códigos.

Lambda Expressions como dado

Em algumas circunstâncias, podemos usar a sintaxe Lambda para representar uma expressão que desejamos manipular como dado no lugar do resultado da expressão em si. Veja o trecho de código, extraído dos testes do EasyMock, que segue:

1 var target = new FluentMock<IFoo>() 2 .Setup((foo) => foo.Process("1")) 3 .Returns(1) 4 5 .Setup((foo) => foo.Process("2")) 6 .Returns(1500) 7 8 .Setup((foo) => foo.Process(null)) 9 .Throws<ArgumentNullException>() 10 11 .CreateObject();

Observe as linhas 2, 5 e 8. Na prática, as chamadas para o método Process nunca serão realizadas. Por “baixo do capô”, o framework analisa a expressão para criar o mapeamento necessário. O mesmo ocorre, por exemplo, com o método Map do NHibernate. No caso do EasyMock, essa técnica garante que todos os métodos sejam validados em tempo de compilação e que todo o código seja editado com apoio do IntelliSense.

Suporte a passagem de blocos de código como parâmetro

Uma das melhores características da programação funcional está relacionado ao conceito de Higher Order Functions. Como bem apresentado por Joel Spolsky no provocativo “Can Your Programming Language Do This?”, podemos reduzir a duplicação de código utilizando algum mecanismo de composição.

É comum encontrarmos longas sequências de código quase idênticas onde a “diferença” está em umas poucas linhas de código. Uma das maneiras de se evitar esse problema no mundo orientado a objetos é fazer herança usando um template method. Entretanto, cada vez mais, eu penso que passar o “bloco de código recheio” como parâmetro seja uma abordagem superior.

Além disso, podemos “enxugar” o código significativamente. Repare essa construção simples:

1 for (int i = 0; i < 10; i++) 2 { 3 Console.WriteLine(i); 4 }

Pode ser escrito assim:

1 Enumerable.Range(0, 10).ForEach((i) => Console.WriteLine(i));

Delayed Execution

Outra “capacidade” muito importante para programação funcional é a capacidade de agendar a execução de um código. Ou seja, permitir que um código seja executado em um momento diferente ao que aparece no programa. Segue outro trecho de código extraído do EasyMock que apresenta esse conceito:

1 var target = new FluentMock<IFoo>() 2 3 .Setup((foo) => foo.Process(It.IsAny<int>())) 4 .Callback(() => before = true) 5 .Returns(1) 6 .Callback(() => after = true) 7 8 .CreateObject();

Os trechos de código em passados como parâmetro em Callback não são executados nesse instante.

Memoization

Nome complicadinho, não acha? Trata-se de uma técnica usada para evitar execução de chamadas “pesadas” reutilizando o resultado de execuções prévias. Um bom exemplo da técnica pode ser encontrado em Fibonacci Numbers, Caching and Closures de Dustin Campbell. Aqui uma transposição da implementação que ele apresentou sem uso da técnica:

1 static long g_Steps; 2 3 static int Fibonacci(int n) 4 { 5 g_Steps++; 6 7 if (n < 2) 8 return n; 9 else 10 return Fibonacci(n - 1) + Fibonacci(n - 2); 11 } 12 13 static void Main(string[] args) 14 { 15 for (int i = 0; i <= 45; i++) 16 { 17 g_Steps = 0; 18 Stopwatch sw = Stopwatch.StartNew(); 19 int fib = Fibonacci(i); 20 sw.Stop(); 21 22 Console.WriteLine( 23 "Fibonacci({0}) = {1:###,###,###,##0} [{2:###,###,###,##0} steps] [{3:0.###} seconds]", 24 i, fib, g_Steps, (float)sw.ElapsedMilliseconds / 1000); 25 } 26 27 Console.ReadLine(); 28 }

Implementando memória  …

1 static Dictionary<int, int> g_results = new Dictionary<int, int>(); 2 3 static int Fibonacci(int n) 4 { 5 g_Steps++; 6 7 if (n < 2) 8 return n; 9 else 10 { 11 if (g_results.ContainsKey(n)) return g_results[n]; 12 var result = Fibonacci(n - 1) + Fibonacci(n - 2); 13 g_results.Add(n, result); 14 return result; 15 } 16 }

Que diferença? Bem, sem memória, alguns minutos. Com memória, instantâneo.

Map/Reduce Pattern

Para quem gosta de Python (e outras que suportam esse padrão), LINQ trouxe uma ótima novidade. Map = Select e Reduce = Aggregate. Um exemplo simples. Somar todos os numeros de uma lista:

1 // abordagem tradicional 2 sum = 0; 3 for (int i = 0; i < numbers.Length; i++) 4 sum += numbers[i]; 5 6 // usando aggregate 7 sum = numbers.Aggregate((a, b) => a + b); 8 9 // usando sum 10 sum = numbers.Sum();

Observe que o Extension Method Sum é uma especialização de Aggregate. Outro aspecto importante é que podemos, usando o método Select, adicionar filtros rapidamente a consulta.

Bem, por hoje, é isso Smiley piscando

3 comentários em “Functional Programming–Outras considerações

  1. Pingback: Memoization « Paulo Pellucci

  2. Pingback: Como se fosse a primeira vez… « Elemar DEV

  3. Pingback: Functional Programming (Memoization) em JavaScript « Elemar DEV

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

Informação

Publicado às 24/08/2010 por em Post e marcado , .

Estatísticas

  • 677,340 hits
%d blogueiros gostam disto: