Elemar DEV

Tecnologia e desenvolvimento

Haskell para programadores C# – parte 7 – Higher-order functions

Olá pessoal. Tudo certo?!

Hoje falaremos sobre Higher-order functions. Elas permitem que alguns patterns de execução sejam implementados de forma mais elegante, como funções.

Como sempre, para facilitar o entendimento, tentarei construir código em C# com funcionalidade semelhante.

Se você está chegando agora, considere ver os outros posts dessa série.

O que são Higher-order functions?!

Higher-order functions são funções que (1) recebem funções em seus argumentos; ou (2) retornam funções.

Considere o seguinte código em C#:

var teenagers = people.Where(person => person.Age >= 13 && person.Age <= 19);

Nesse código, Where é uma Higher-order function pois recebe uma função (expressão lambda) como argumento.

Pegou a idéia!?

Higher-order functions são um conceito comum em Haskell

Considere o seguinte código em Haskell:

add a b = a + b

Como já expliquei em posts anteriores. Esse código equivale a:

add = \a -> \b -> a + b

Que escrito em C#, seria assim:

Func<int, Func<int, int>> add = a => b => a + b;

Logo, Add é uma Higher-Order Function, pois, como pode perceber, ela retorna uma função.

Um exemplo simples

Considere a seguinte Higher-order function.

Em Haskell:

twice :: (a -> a) -> a -> a
twice f x = f (f x)

Como pode ver, no tipo, trata-se de uma função que: (1) espera uma função como primeiro argumento, (2) espera um segundo argumento qualquer [generic] e (3) retorna o um valor do mesmo tipo que seu segundo argumento. Trata-se de uma Higher-order function.

Em C#:

static T Twice<T>(Func<T, T> f, T x)
{
    return f(f(x));
}

Executando:

image

Mesmo código em C#?

var result1 = Twice(a => a * 2, 5);
var result2 = Twice(a => a + 1, 1);

map/Select

Haskell define funções poderosas utilizando o conceito de High-Order Functions. Muitas dessas funções têm equivalente em LINQ.

Comecemos com a função map . Ela faz parte das funções built-in do Haskell, mas, poderia ser definida assim:

map :: (a -> b) -> [a] -> [b]
map f xs = [f x | x <- xs] 

O equivalente dela, em C#, seria a função Select do Linq.

 image

Esse exemplo, em C#

Enumerable.Range(1, 5).Select(a => a + 1);

Mais alguns exemplos:

image

Comprimento de uma lista?!

Em Haskell:

length' :: [a] -> Int
length' xs = sum (map (\_ -> 1) xs)

Em C#:

static int Length<T>(IEnumerable<T> source)
{
    return source.Select(a => 1).Sum();
}

filter/Where

Outra Higher-order function “built-in” em Haskell, extremamente útil, é filter. Veja sua definição:

filter :: (a -> Bool) -> [a] -> a 
filter p xs = [x | x <- xs, p x]

O equivalente dela, em C#, seria a função Where do Linq

Veja alguns exemplos de uso:

image

Em C#:

Enumerable.Range(1, 10).Where(x => x%2 == 0);
Enumerable.Range(1, 10).Where(x => x > 5);
new string("String With Spaces".Where(c => c != ' ').ToArray());

all, any, takeWhile, dropWhile

Mais algumas higher-order functions “built-in” em Haskell:

all :: (a -> Bool) -> [a] -> Bool
all p xs = and [p x | x <- xs]

any :: (a -> Bool) -> [a] -> Bool
any p xs = or [p x | x <- xs]

takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile p (x:xs) 
	| p x = x : (takeWhile p xs)
	| otherwise = []

dropWhile :: (a -> Bool) -> [a] -> [a]
dropWhile p (x:xs)
	| p x = dropWhile p xs
	| otherwise = xs

Exemplos de uso:

image

 

LINQ oferece implementações para todas essas funções. São elas: All , Any, TakeWhile e SkipWhile.

Deixo para você o desafio de tentar reescrever essas funções.

foldr/Aggregate

Outra Higher-order function, extremamente poderosa, definida em Haskell e com equivalente em LINQ é foldr. Eis sua definição:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)

Trata-se de uma função complexa, mas com aplicação muito comum.

Repare nesses exemplos inpirados no post anterior:

product' :: Num a => [a] -> a
product' [] = 1
product' (n:ns) = n * product' ns

sum' :: Num a => [a] -> a
sum' [] = 0
sum' (n:ns) = n + sum' ns

and' :: [Bool] -> Bool
and' [] = True
and' (x:xs) = x && and' xs

or' :: [Bool] -> Bool
or' [] = False
or' (x:xs) = x || and' xs

Perceba como há um pattern em todas essas recursões. foldr simplifica a aplicação desse pattern. Usando foldr, podemos reescrever todas essas funções assim:

product' :: Num a => [a] -> a
product'= foldr (*) 1

sum' :: Num a => [a] -> a
sum'= foldr (+) 0

and' :: [Bool] -> Bool
and'= foldr (&&) True

or' :: [Bool] -> Bool
or'= foldr (||) False

Coisa linda, operadores em Haskell serem funções. Não acha?!

O equivalente em LINQ para foldr é a função Aggregate. Veja:

Func<IEnumerable<int>, int> sum = xs => xs.Aggregate(0, (a, b) => a + b);
Func<IEnumerable<int>, int> product = xs => xs.Aggregate(1, (a, b) => a * b);
Func<IEnumerable<bool>, bool> and = xs => xs.Aggregate(true, (a, b) => a && b);
Func<IEnumerable<bool>, bool> or = xs => xs.Aggregate(false, (a, b) => a || b);

Era isso.

2 comentários em “Haskell para programadores C# – parte 7 – Higher-order functions

  1. Pingback: Haskell para programadores C# – parte 8 – Composition « Elemar DEV

  2. Pingback: Haskell para programadores C# – parte 9 – Modules « 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 16/06/2012 por em Post e marcado , , .

Estatísticas

  • 625,873 hits
%d blogueiros gostam disto: