¡Esta es una revisión vieja del documento!
Notar que en todos los casos, cuando queremos hablar de un rango de números, indicamos sus extremos como $[A, B)$, es decir, utilizamos como extremos dos valores A
y B
de modo tal que los números del rango son aquellos $x$ que cumplen $A \leq x < B$. Esta elección de “fronteras”, que es cerrada a la izquierda, pero abierta a la derecha, es para casi toda tarea de programación la más conveniente, y se utiliza mucho en los lenguajes de programación como C++. También es la que más recomendamos, porque evita muchos errores y es más clara que otras, una vez que uno la conoce.
Para iterar los números desde 0
hasta n-1
inclusive, con la variable i
que es creada para el for:
#define forn(i,n) for(int i=0;i<int(n);i++)
Para iterar los números desde s
hasta n-1
inclusive, con la variable i
que es creada para el for:
#define forsn(i,s,n) for(int i=int(s);i<int(n);i++)
Para iterar los números desde n-1
hasta 0
inclusive, bajando con la variable i
que es creada para el for:
#define dforn(i,n) for(int i=int(n)-1;i>=0;i--)
Para iterar los números desde n-1
hasta s
inclusive, bajando con la variable i
que es creada para el for:
#define dforsn(i,s,n) for(int i=int(n)-1;i>=int(s);i--)
Las siguientes macros son cómodas para iterar una colección hacia adelante o hacia atrás, si nos interesa tener acceso al iterador y no solamente a los elementos iterados. Eran necesarias (utilizando typeof
en lugar de auto
) antes de c++11 para iterar sets y maps, pues no existía el foreach
.
#define forall(i,c) for(auto i = (c).begin(); i != (c).end(); i++) #define dforall(i,c) for(auto i = (c).rbegin(); i != (c).rend(); i++)
La siguiente es muy útil para utilizar con funciones de STL, donde se suele pedir un rango mediante dos iteradores, para pasar directamente una colección completa.
#define all(c) (c).begin(),(c).end()
El uso más común de esta macro, es para llamar a la función sort
, con sort(all(v))
por ejemplo, siendo v
un vector. Sin embargo es útil con muchísimas funciones de la STL, como podría ser por ejemplo una instrucción find(all(v), 27)
.
La siguiente sirve para consultar si un elemento está en un set (o map: en un map, se consulta si el elemento dado es una clave del diccionario).
#define esta(x,c) ((c).find(x) != (c).end())
Las siguientes son muy útiles para buscar y corregir errores en programas:
#define DBG(x) cerr << #x << " = " << (x) << endl #define RAYA cerr << "===============================" << endl
DBG es una macro particularmente bonita y “mágica”. Si escribimos en una línea DBG(x);
en el código, siendo x
una variable (o podría ser una expresión), nos mostrará su valor y su nombre por pantalla. Alentamos a probarla para ver su utilidad a la hora de buscar bugs en programas.
Similarmente, RAYA;
muestra una clara línea separadora: esto es especialmente útil cuando tenemos un for
y queremos mostrar los valores de varias variables en cada iteración. Poniendo un RAYA;
, podemos separar con facilidad los valores entre iteraciones.
Los siguientes typedef
son razonables y cómodos en programación competitiva:
typedef long long tint; typedef long double tdbl; typedef vector<int> vint; typedef pair<int,int> pint; typedef pair<tint,tint> ptint;
#define forn(i, n) for(int i = 0; i < int(n); i++)
nos permite, además de parametrizar la cantidad de iteraciones, parametrizar la variable que indexa. Entonces uno puede escribir
forn(i, 10){ forn (j, v.size()) { } }
(Comentario: en la declaración de la macro hay un int(n)
que nos permite que el forn
hasta v.size()-1
y similares expresiones no tenga un posible bug causado por ser este valor de tipo unsigned, y de paso logra que el compilador no emita un warning por esto.)
Uno podría entusiarmarse con las macros y hacer la siguiente macro:
#define forn(n) for(int i = 0; i < int(n); i++)
Es una mala idea definir el forn
sin pasarle la variable para indexar, porque eso nos podria introducir “bugs ocultos” por no ser suficientemente declarativos y esconder cosas en la macro. En este caso, escondemos una declaración de variables.
Por ejemplo, este código tendría un error oculto usando esa macro
int i = 24; forn(10){ cout << i << endl; }
Un lector cualquiera asumiría que este código escribe 10 veces el número 24, pero se encontraría con que imprime los números del 1 al 10.
En general el objetivo de las macros no es escribir menos caracteres, sino escribir código que evite bugs simples por repetición o copy-paste.
for(int i=0;i<n;i++){ for(int j=0;j<m;i++){ } }
Ese código tiene un bug no evidente, en el segundo for incrementa i en vez de j. En cambio, usando la macro forn como la definimos más arriba, sería
forn(i, n) { forn(j, m) { } }
¡Donde ese bug es simplemente imposible de escribir!