Tuesday, August 19, 2008 6:58 PM

Antes de continuar falando sobre Linq vou apresentar como ficariam os outros dois exemplos típicos de High-order functions seguindo a estrutura do Filter.

Map

Uma função que dada uma coleção de objetos de um tipo, retorna uma coleção de objetos de outro tipo, realizando a transformação.

Um exemplo é a integração entre dois aplicativos. É normal que entidades comuns, como Cliente, co-existam nas duas aplicações. Também é normal que esta entidade seja exatamente igual nos dois aplicativos. Daí a necessidade de transformações quando os sistemas estiverem trocando informações, desde formato de data, até normalização de tabelas.

Neste caso, o cliente em um dos aplicativos tem nome e sobrenome, já no outro, existe somente o nome completo. Ao trocar informações esta transformação é necessária.

private List<Cliente> MapClientes(List<ClienteCrm> collection)
{
    List<Cliente> result = new List<Cliente>();

    foreach (var item in collection)
    {
        result.Add(new Cliente(string.Format("{0} {1}", item.FirstName, item.LastName)));
    }

    return result;
}

Não é necessário seguir passo-a-passo, pois já fizemos isto no primeiro post, com o Filter. Isolando as partes que não mudam, das mutáveis, tenho o seguinte método para mapear qualquer par de objetos:

public delegate TResult Transform<TSource, TResult>(TSource source);

public static IEnumerable<TResult> Map<TSource, TResult>(this IEnumerable<TSource> collection, 
    Transform<TSource, TResult> transform)
{
    List<TResult> result = new List<TResult>();

    foreach (var item in collection)
    {
        result.Add(transform(item));
    }

    return result;
}

O código que utiliza este método seria:

Cliente MapClienteCrmToCliente(ClienteCrm clienteCrm)
{
    return new Cliente(string.Format("{0} {1}",
    clienteCrm.FirstName,
    clienteCrm.LastName));
}

List<Cliente> clientes = (List<Cliente>) clientesCrm.Map<ClienteCrm, Cliente>(MapClienteCrmToCliente);

Fold ou Reduce

Uma função que processa uma coleção de objetos retornando um valor. Este processamento de alguma forma combina os objetos desta coleção para calcular o resultado final. A mais óbvia seria a somatória, dada uma lista de inteiros retorne a soma destes.

Outro exemplo simples, mas um pouco menos óbvio, seria calcular o valor total de uma venda, que é a soma dos valores totais de cada uma de suas linhas, que é a quantidade de produtos multiplicada pelo preço unitário. O código ficaria assim:

public class SalesOrder
{
    private List<SalesLine> lines = new List<SalesLine>();

    public IEnumerable<SalesLine> Lines
    {
        get
        {
            return lines;
        }
    }

    public double TotalAmount()
    {
        double total = 0;

        foreach (var item in lines)
        {
            total = total + (item.Quantidade * item.Preco);
        }

        return total;
    }
}

Seguindo o mesmo processo de sempre, eu teria um método Fold genérico:

public delegate TResult Acumulate<TSource, TResult>(TSource item, TResult initial);

public static TResult Fold<TSource, TResult>(this IEnumerable<TSource> collection, TResult initial, Acumulate<TSource, TResult> acumulate)
{
    TResult result = initial;

    foreach (var item in collection)
        result = acumulate(item, result);

    return result;
}

E sua utilização seria:

    public double TotalAmount()
    {
        return lines.Fold<SalesLine, double>(0d, AcumulaTotalDaLinha);
    }

    private static double AcumulaTotalDaLinha(SalesLine item, double total)
    {
        return total + (item.Quantidade * item.Preco);
    }

No final das contas esta seria a classe completa, com os três extension methods.

using System;
using System.Collections.Generic;
using System.Linq;

public static class HighOrderExtension
{
    public static IEnumerable<T> Filter<T>(this IEnumerable<T> collection, 
                                            Predicate<T> filter)
    {
        var result = from item in collection
                     where filter(item)
                     select item;

        return result.ToList<T>();
    }

    public delegate TResult Transform<TSource, TResult>(TSource source);

    public static IEnumerable<TResult> Map<TSource, TResult>(this IEnumerable<TSource> collection, 
                                                                Transform<TSource, TResult> transform)
    {
        List<TResult> result = new List<TResult>();

        foreach (var item in collection)
        {
            result.Add(transform(item));
        }

        return result;
    }

    public delegate TResult Acumulate<TSource, TResult>(TSource item, TResult initial);

    public static TResult Fold<TSource, TResult>(this IEnumerable<TSource> collection, TResult initial, Acumulate<TSource, TResult> acumulate)
    {
        TResult result = initial;

        foreach (var item in collection)
            result = acumulate(item, result);

        return result;
    }
}

Até a próxima.

< Exemplos >

Comments

No comments posted yet.
Post Comment
Title *
Name *
Email (never displayed)
Website
Comment * (Allowed tags: blockquote, a, strong, em, p, u, strike, super, sub, code)  
Please add 1 and 1 and type the answer here: