Biblioteca chrono - Ejemplo de uso: contando los días laborables en un período

En este post analizaremos el empleo de la biblioteca estándar <chrono> [1] para la codificación de un algoritmo sencillo que permita determinar el número de días laborables en un período temporal dado. En nuestros ejemplos haremos uso, entre otras, de la función std::chrono::parse [2] incluida en C++20 y que cuenta con una implementación experimental en el compilador GCC 14.

A modo de caso concreto de estudio, nos centraremos en el ámbito educativo a fin de computar los días lectivos dentro de cualquier intervalo cerrado [sd_0, sd_1], siendo sd_0 y sd_1 dos fechas enmarcadas en un curso académico que cumplan sd_0 ≤ sd_1. Como días lectivos asumiremos todos aquéllos de la semana exceptuando sábados, domingos y festivos.

tl-ranges - Instalación bajo mingw-w64

La biblioteca tl-ranges de Sy Brand [1], distribuida bajo una licencia de dominio público, proporciona una excelente implementación de adaptadores de rangos que no llegaron a ser incluidos en C++20. Entre ellos destacaríamos (los elementos estandarizados en C++23 se indican con un asterisco):

C++23 - Generador síncrono std::generator

El futuro estándar del lenguaje C++23 incorporará la plantilla de clase std::generator (cabecera <generator>) como modo de obtener generadores síncronos basados en corrutinas que modelen el concepto std::ranges::input_range (recordemos que los algoritmos que operan con este tipo de rangos no deben intentar atravesar un mismo iterador input_range más de una vez). Dichos generadores constituyen vistas que sólo pueden moverse (move-only views), con iteradores de igual naturaleza [1].

Como primer ejemplo, consideremos el siguiente generador de números enteros pertenecientes a la sucesión de Fibonacci, es decir, aquélla que comienza con los números 0 y 1 y para la que cada valor subsiguiente se calcula como suma de los dos elementos que lo preceden. Por sencillez, ignoraremos el inevitable desbordamiento de enteros para valores elevados de la sucesión [1]:

   auto fibonacci_gen() -> std::generator<int>    {       auto a = 0,            b = 1;       while (true) {          co_yield std::exchange(a, std::exchange(b, a + b));       }    }

"Hello, world!" en C++23

C++ ha conocido dos estándares en su etapa moderna que, por su alcance y extensión, han revolucionado la manera de programar en el lenguaje. Nos referimos, por supuesto, a C++11 y C++20. El próximo estándar del lenguaje, C++23, puede considerarse una actualización relativamente menor de C++20, de forma análoga a como C++14 y C++17 constituyeron extensiones naturales de C++11. Aun cuando la pandemia por COVID-19 de los últimos años haya afectado al proceso de trabajo habitual del comité ISO WG21, C++23 logrará publicarse en plazo presentando numerosas novedades, entre las que cabe señalar:

  • A nivel del núcleo del lenguaje: if consteval [1], deducción de this [2], atributos en expresiones lambda [3], atributo [[assume]] [4], auto(x) y auto{x} [5], directiva #warning [6].
  • A nivel de la biblioteca estándar: nuevas vistas std::views [7], <expected> [8], operaciones monádicas para std::optional [9], vistas para arrays multidimensionales <mdspan> [10], adaptadores de contenedores asociativos <flat_map> y <flat_set> [11, 12], generadores síncronos std::generator como soporte para corrutinas [13], funciones de impresión std::print y std::println en <print> [14].

Mónadas para programadores en C++: std::optional (Parte I)

Conceptos matemáticos relevantes: Categorías, functores y mónadas

Desde un punto de vista matemático, una categoría C está constituida por [1]:
  1. Una colección Ob(C) de objetos.
  2. Una colección Hom(C) de flechas o morfismos que enlazan dichos objetos. Un morfismo f del objeto X al objeto Y se denota f: X → Y. El conjunto de morfismos entre los objetos X e Y se denota HomC(X, Y).
  3. Una operación binaria de composición de morfismos. La composición de f: X → Y con g: Y → Z se denota g ∘ f: X → Z
Asumimos los siguientes axiomas en relación a la composición:
  • Asociatividad: Si f: A → Bg: B → C h: C → D, entonces h ∘ (g ∘ f) = (h ∘ g) ∘ f.
  • Para cada objeto X, existe un morfismo identidad idXX → X que se comporta como elemento neutro bajo composición, i.e., para todo morfismo f: X → Y se cumple f ∘ idX idY  f = f. Se puede demostrar que idX es único para cada X.

C++23 - Vista chunk_by

Introducción

El futuro estándar del lenguaje C++23, actualmente en estado feature freeze, proporciona nuevas y útiles vistas para su biblioteca de rangos, entre ellas [1]:

  • std::views::adjacentstd::views::adjacent_transform
  • std::views::cartesian_product
  • std::views::chunkstd::views::chunk_by
  • std::views::join_with
  • std::views::repeat
  • std::views::slide
  • std::views::stride
  • std::views::zip, std::views::zip_transform

La vista std::views::chunk_by, centro de atención de este post, agrupa elementos contiguos de un rango a través de un predicado binario.  Así, dado un rango de partida R=[T] de elementos de tipo T y un predicado binario P, la vista devolverá un rango de rangos [[T]] donde cada subrango contiene elementos contiguos de R que cumplen la siguiente condición: para cada elemento ei en el subrango a excepción del primero, la evaluación del predicado para el elemento inmediatamente anterior ei-1 y ei es verdadera, i.e., P(ei-1,ei)=true [2, 3]. Es importante señalar que no se requiere que dicho predicado constituya una relación de equivalencia.