Programação Funcional em C++: do A ao Z
Muitos desenvolvedores familiarizados com Python ou JavaScript estão acostumados com ferramentas poderosas de programação funcional: lambdas, map, filter, reduce. Mas você sabia que C++ oferece possibilidades igualmente flexíveis para o estilo funcional? Neste artigo, vamos explorar a programação funcional em C++, desde conceitos básicos até técnicas avançadas. Você aprenderá a escrever código mais conciso, seguro e expressivo usando os recursos modernos do C++11/14/17.
1. Fundamentos da Abordagem Funcional: Expressões Lambda e std::function
A programação funcional em C++ começa com expressões lambda. Elas permitem criar funções anônimas diretamente no ponto de uso. A sintaxe de uma lambda é simples:
// Lambda básicaauto sum = [](int a, int b) -> int { return a + b; };std::cout << sum(5, 3); // 8
// Lambda com captura de variáveisint multiplier = 3;auto multiply = [multiplier](int x) { return x * multiplier; };std::cout << multiply(7); // 21A principal característica das lambdas é a captura de contexto. Você pode capturar variáveis por valor ([=]), por referência ([&]) ou de forma combinada. Esta é a base das closures, que são o fundamento do estilo funcional.
Para armazenar e transmitir lambdas, usa-se std::function — um wrapper polimórfico para objetos invocáveis:
#include <functional>
std::function<int(int, int)> operation;
operation = [](int a, int b) { return a + b; };std::cout << operation(10, 20); // 30
operation = [](int a, int b) { return a * b; };std::cout << operation(10, 20); // 200Dica: Use auto para lambdas se não precisar armazená-las como tipo. std::function adiciona uma pequena sobrecarga, portanto deve ser usado apenas quando houver necessidade de polimorfismo.
2. Algoritmos STL: map, filter, reduce feitos à mão
Em C++ não existem funções nativas map, filter, reduce como em Python, mas a STL fornece algoritmos poderosos que as substituem. Vamos ver os principais:
2.1. Map — transformação de elementos
Em vez de map, use std::transform:
#include <algorithm>#include <vector>
std::vector<int> numbers = {1, 2, 3, 4, 5};std::vector<int> squares(numbers.size());
std::transform(numbers.begin(), numbers.end(), squares.begin(), [](int n) { return n * n; });// squares: {1, 4, 9, 16, 25}2.2. Filter — filtragem por condição
O análogo ao filter é std::copy_if ou std::remove_if (com remoção posterior):
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};std::vector<int> evens;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(evens), [](int n) { return n % 2 == 0; });// evens: {2, 4, 6}2.3. Reduce — redução da coleção
Para reduce, use std::accumulate de <numeric>:
#include <numeric>
std::vector<int> numbers = {1, 2, 3, 4, 5};int sum = std::accumulate(numbers.begin(), numbers.end(), 0, [](int acc, int n) { return acc + n; });// sum = 15Exemplo avançado: Combinando map, filter e reduce para processar dados:
std::vector<std::string> names = {"Alice", "Bob", "Charlie", "David"};// Obtendo a soma dos comprimentos dos nomes que começam com 'A' ou 'C'int result = std::accumulate(names.begin(), names.end(), 0, [](int acc, const std::string& name) { if (name[0] == 'A' || name[0] == 'C') { return acc + name.length(); } return acc;