En C++, el operador | siempre existió.
Tradicionalmente lo conocemos como el operador OR a nivel de bits:
int result = 5 | 3;
Pero con Ranges, el operador | también permite construir pipelines de transformaciones.
Por ejemplo:
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto result = numbers
| std::views::filter([](int x) {
return x % 2 == 0;
})
| std::views::transform([](int x) {
return x * 10;
});
for (auto value : result) {
std::cout << value << " ";
}
}
Resultado:
20 40
Podemos leer este código de izquierda a derecha:
numbers
|
filter
|
transform
Primero:
[1, 2, 3, 4, 5]
Aplicamos:
filter(x -> x es par)
Resultado:
[2, 4]
Luego:
transform(x -> x * 10)
Resultado:
[20, 40]
El código:
numbers
| std::views::filter(...)
| std::views::transform(...);
representa un pipeline.
Cada operación recibe el resultado de la operación anterior.
El operador | sigue siendo el mismo operador definido por C++.
Lo que cambia es que la biblioteca de Ranges utiliza operator overloading para darle un nuevo comportamiento cuando trabajamos con vistas.
Conceptualmente:
numbers | operation
es similar a escribir:
operation(numbers)
Por ejemplo:
numbers
| std::views::filter(isEven)
podemos imaginarlo como:
std::views::filter(numbers, isEven);
La ventaja del operador | es la legibilidad.
Comparemos:
auto result =
std::views::transform(
std::views::filter(numbers, isEven),
multiplyByTen
);
Con:
auto result =
numbers
| std::views::filter(isEven)
| std::views::transform(multiplyByTen);
Ambas expresan la misma idea, pero el pipeline permite leer el código en el orden en que ocurren las transformaciones.
Este estilo puede resultar familiar para quienes trabajan con otros lenguajes.
Por ejemplo, en F#:
data
|> filter
|> transform
O en Elixir:
data
|> filter()
|> transform()
En C++:
data
| std::views::filter(...)
| std::views::transform(...);
La idea es similar:
dato → operación → operación → resultado
En un post anterior vimos que C++23 no tiene una operación llamada flatMap.
Pero podemos construirla con un pipeline:
auto result = numbers
| std::views::transform([](int x) {
return std::vector{x, x * 10};
})
| std::views::join;
Podemos leerlo como:
numbers
|
transform
|
join
Es decir: flatMap = transform + join
C++23 no se convirtió en un lenguaje funcional.
Pero con Ranges y Views, C++ permite escribir código mucho más declarativo y expresar transformaciones sobre datos como pipelines.
Y quizás esa sea una de las características más interesantes del C++ moderno.

No hay comentarios.:
Publicar un comentario