Programación funcional en C++: de la A a la Z
Muchos desarrolladores familiarizados con Python o JavaScript están acostumbrados a las potentes herramientas de programación funcional: lambdas, map, filter, reduce. Pero, ¿sabías que C++ ofrece capacidades igualmente flexibles para el estilo funcional? En este artículo, exploraremos la programación funcional en C++ desde los conceptos básicos hasta las técnicas avanzadas. Aprenderás a escribir código más conciso, seguro y expresivo utilizando las características modernas de C++11/14/17.
1. Fundamentos del enfoque funcional: expresiones lambda y std::function
La programación funcional en C++ comienza con las expresiones lambda. Permiten crear funciones anónimas directamente en el punto de uso. La sintaxis de una lambda es simple:
// Lambda básicaauto sum = [](int a, int b) -> int { return a + b; };std::cout << sum(5, 3); // 8
// Lambda con captura de variablesint multiplier = 3;auto multiply = [multiplier](int x) { return x * multiplier; };std::cout << multiply(7); // 21La característica clave de las lambdas es la captura del contexto. Puedes capturar variables por valor ([=]), por referencia ([&]) o de forma combinada. Esta es la base de los closures, que son el fundamento del estilo funcional.
Para almacenar y pasar lambdas se utiliza std::function, un envoltorio polimórfico para objetos invocables:
#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); // 200Consejo: Usa auto para lambdas si no necesitas almacenarlas como tipo. std::function añade una pequeña sobrecarga, por lo que debe usarse solo cuando se requiere polimorfismo.
2. Algoritmos STL: map, filter, reduce hechos a mano
En C++ no hay funciones integradas map, filter, reduce como en Python, pero STL proporciona potentes algoritmos que las reemplazan. Veamos los principales:
2.1. Map — transformación de elementos
En lugar de map, usa 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 — filtrado por condición
El análogo de filter es std::copy_if o std::remove_if (con posterior eliminación):
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 — reducción de la colección
Para reduce usa 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 = 15Ejemplo avanzado: Combinamos map, filter y reduce para procesar datos:
std::vector<std::string> names = {"Alice", "Bob", "Charlie", "David"};// Obtenemos la suma de las longitudes de los nombres que comienzan con 'A' o '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;