- La Programación Competitiva es un deporte mental que nos permite codificar un problema dado bajo las restricciones establecidas. El propósito de este artículo es guiar a cada individuo sobre cómo pueden depurar su código de manera eficiente durante un concurso.
Requisito previo: Configuración de Sublime Text para el entorno de programación competitivo de C++
El tiempo es algo precioso e importa mucho durante un concurso de codificación. Al escribir un código, surgen errores y los programadores suelen pasar mucho tiempo depurándolo. A menudo, los programadores se ocupan de estructuras de datos complejas durante un concurso y es necesario depurarlas en el tiempo límite.
Este artículo se enfoca en cómo depurar el código fuente de C++ de manera eficiente en Sublime Text (IDE) durante un concurso y ahorrar tiempo. En primer lugar, se requiere configurar la estructura de archivos de nuestro Sublime Text. A continuación se muestran los pasos para configurar la estructura de archivos de Sublime Text.
Paso 1: Abra Sublime Text y siga los pasos a continuación:
1. Crea tres archivos:
- file.cpp: El archivo para escribir el código.
- inputf.txt: El archivo donde estaremos dando la entrada.
- outputf.txt: El archivo donde se mostrará la salida.
2. Ahora, realiza los siguientes pasos:
- Seleccione Ver > Diseño > Columnas: 3 . Esto creará tres columnas en el espacio de trabajo. Mueva los tres archivos en tres columnas.
- Seleccione Ver > Grupos > Máximo de columnas: 2: input.txt y output.txt se apilarán en una sola columna.
Su Sublime Text se vería similar a esto:
Paso 2: crea una función local fuera de la función principal. Se utiliza para recibir entradas del archivo input.txt y mostrar la salida en el archivo output.txt. A continuación se muestra el fragmento de código C++ para el mismo.
C++
// Declare this function outside // the main function void local() { // In case of online judges (like // codechef, codeforces etc) these // lines will be skipped. In other // words these lines would be executed // in Sublime Text only #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); // ONLINE_JUDGE #endif }
Paso 3: Llamar desde la función principal:
// Call from the main function local();
Combinando los pasos anteriores nuestro programa completo sería:
C++
// C++ program to implement // the above approach #include <bits/stdc++.h> using namespace std; // Local function void local() { #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); // ONLINE_JUDGE #endif } // Driver code int main() { local(); return 0; }
Paso 4: Ahora el IDE se vería similar a esto:
Depuración usando la función de impresión:
Cree una función de impresión en nuestro programa cada vez que necesitemos imprimir una variable o cualquier estructura de datos como vector, conjunto, mapa, etc. A continuación se muestra el programa C++ para implementar el mismo enfoque:
C++
// C++ program to implement // the above approach #include <bits/stdc++.h> using namespace std; // Driver code int main() { // Initializing a vector vector<int> vect = {2, 4, 10, 12, 17}; // First operation for (auto& x : vect) { if (x % 2 == 0) x += 10; else x -= 10; } // Second operation for (auto& x : vect) x += 2; // Third operation for (auto& x : vect) x += 20; }
Supongamos que algo salió mal en nuestra lógica debido a que no se obtiene el resultado deseado durante un concurso y, por lo tanto, para verificar el estado del vector después de la primera operación, se puede crear una función de impresión fuera de la función principal que acepta un vector.
C++
// print function outside the // main function void print(vector<int>& vect) { cout << "vect " << ' '; cout << '[' << ' '; // Print vector elements for (auto x : vect) { cout << x << ' '; } cout << ']'; }
Siempre que sea necesario comprobar los elementos del vector, se puede llamar a la función imprimir() pasando el vector como argumento a la función de impresión.
// Calling print function from main print(vect);
A continuación se muestra el programa C++ completo para ilustrar cómo implementar el concepto anterior:
C++
// C++ program to implement // the above concept #include <bits/stdc++.h> using namespace std; // Print function for debugging void print(vector<int>& vect) { cout << "vect " << ' '; cout << '[' << ' '; // Print vector elements for (auto x : vect) { cout << x << ' '; } cout << ']'; } // Driver code int main() { // Initializing a vector vector<int> vect = {2, 4, 10, 12, 17}; // First operation for (auto& x : vect) { if (x % 2 == 0) x += 10; else x -= 10; } // Printing vect elements after // applying first operation // Checking the status of vect as // a part of debugging print(vect); // Second operation for (auto& x : vect) x += 2; // Third operation for (auto& x : vect) x += 20; int finalAnswer = 0; for (auto x : vect) finalAnswer += x; // Print the final answer cout << "\nFinal Answer: " << finalAnswer; return 0; }
vect [ 12 14 20 22 7 ] Final Answer: 185
Desventajas de este método:
- Para la misma estructura de datos pero con diferentes tipos de datos, es necesario crear múltiples funciones de impresión. Por ejemplo, hay un vector de tipo entero y un vector de tipo string, luego para imprimir elementos, se requiere crear dos funciones de impresión fuera de la función principal. Una función de impresión aceptará un vector de tipo entero y otra función de impresión aceptará un vector de tipo string.
- El contenido del vector se imprimiría junto con los valores deseados en el mismo archivo output.txt, lo que podría ser confuso para nosotros.
- Es necesario comentar las declaraciones que se utilizan para llamar a una función de impresión desde la función principal, antes de enviar el archivo de código fuente al juez en línea (Codeforces, Spoj, Codechef, etc.).
Depuración usando plantilla:
En el método anterior, el tipo de datos del vector está codificado. La plantilla se puede utilizar en C++. Una plantilla es una herramienta simple pero muy poderosa en C++. La idea simple es pasar el tipo de datos como un parámetro para que no haya necesidad de escribir el mismo código (función de impresión) para diferentes tipos de datos. A continuación se muestra el fragmento de código C++ para la plantilla:
C++
// One print function works for // all data types. This would work // even for user defined types if // operator '>' is overloaded template <typename T> void print(vector<T> vect) { // body }
A continuación se muestra el programa completo en C++ para ilustrar el concepto anterior:
C++
// C++ program to implement // the above approach #include <bits/stdc++.h> using namespace std; // Using template so that this // function works for all data // types template <typename T> void print( vector<T>& vect) { cout << "vect " << ' '; cout << '[' << ' '; for (auto x : vect) { cout << x << ' '; } cout << ']'; cout << '\n'; } // Driver code int main() { vector<int> vect1 = {2, 4, 10, 12, 17}; for (auto& x : vect1) { if (x % 2 == 0) x += 10; else x -= 10; } // Printing vect1 elements print(vect1); // Initializing a vector of // string type vector<string> vect2 = {"Geeks", "for", "Geeks"}; // Printing vect2 elements print(vect2); // Modifying vect2 // push back string "is great" vect2.push_back("is the great"); // Printing vect2 after modification print(vect2); int finalAnswer = 0; for (auto x : vect1) finalAnswer += x; cout << "Final Answer: " << finalAnswer; return 0; }
vect [ 12 14 20 22 7 ] vect [ Geeks for Geeks ] vect [ Geeks for Geeks is the great ] Final Answer: 75
Se puede hacer algo similar con cualquier estructura de datos como un conjunto , un conjunto múltiple , pares , etc. A continuación se muestra la implementación usando el conjunto:
C++
// C++ program to implement // the above approach #include <bits/stdc++.h> using namespace std; // Using template so that this // function works for all data // types template <typename T> void print( set<T>& set1) { cout << "set " << ' '; cout << '[' << ' '; for (auto x : set1) { cout << x << ' '; } cout << ']'; cout << '\n'; } // Driver code int main() { // Declaring a set set<int> set1; // Inserting elements in the set for (int i = 0; i < 10; i++) set1.insert(i); // Printing set1 elements print(set1); // Declaring another set of // string type set<string> set2; // Inserting elements in the set set2.insert("GeeksforGeeks"); // Printing set2 elements print(set2); int finalAnswer = 0; for (auto x : set1) finalAnswer += x; cout << "Final Answer: " << finalAnswer; return 0; }
set [ 0 1 2 3 4 5 6 7 8 9 ] set [ GeeksforGeeks ] Final Answer: 45
Desventajas de este método:
- El método anterior no es muy eficiente ya que cada vez que se envía el programa, es necesario comentar las declaraciones de impresión dentro de la función principal.
- Los elementos de la estructura de datos se imprimirían junto con otros valores deseados en el mismo archivo output.txt, lo que podría ser confuso para nosotros.
Depuración usando cerr:
La idea es usar la combinación de cerr (flujo de error) y manejo de archivos en el programa. Cree un archivo separado (error.txt) y use cerr stream en lugar de cout stream. Finalmente, con la ayuda del manejo de archivos, imprima el estado de la estructura de datos en el archivo error.txt.
Paso 1: en primer lugar, agregue los siguientes fragmentos fuera de la función principal:
- Para imprimir una variable , podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
//Template definition template <typename T> //Function to print the variable void print(T x) { // Using error stream to print // the variable cerr << x; }
- Para imprimir elementos vectoriales , podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
// Template definition template <typename T> // Function to print the elements // of the vector void print(vector<T>& a) { cerr << '[' << ' '; for (auto x : a) { // Same to print a variable (Function // Overloading) print(x); cerr << ' '; } cerr << ']'; }
- Para imprimir elementos establecidos dispuestos en orden no descendente, podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
//Template definition template <typename T> // Function to print elements of the // set arranged in non-descending order void print(set<T>& a) { cerr << '[' << ' '; for (auto x : a) { // Same as printing a variable // (Function Overloading) print(x); cerr << ' '; } cerr << ']'; }
- Para imprimir elementos establecidos dispuestos en orden no ascendente, podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
template <typename T> // Function to print the set elements // arranged in non-ascending order void print(set<T, greater<T> >& a) { cerr << '[' << ' '; for (auto x : a) { // same as printing a variable // (Function Overloading) print(x); cerr << ' '; } cerr << ']'; }
- Para imprimir elementos de conjuntos desordenados , podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
// Template definition template <typename T> // Function to print unordered // set elements void print(unordered_set<T>& a) { cerr << '[' << ' '; for (auto x : a) { // Same as printing a variable // Using the concept of function // overloading print(x); cerr << ' '; } cerr << ']'; }
- Para imprimir elementos del mapa , podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
//Template definition template <typename T, typename V> //Function to print map elements // arranged in non-descending order void print(map<T, V>& a) { cerr << "[ "; for (auto i : a) { // Same as variable using the // concept of function overloading print(i); cerr << " "; } cerr << "]"; }
- Para imprimir elementos de mapa desordenados , podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
//Template definition template <typename T, typename V> //Function to print unordered map elements void print(unordered_map<T, V>& a) { cerr << "[ "; for (auto i : a) { // Same as variable using the // concept of function overloading print(i); cerr << " "; } cerr << "]"; }
- Para imprimir elementos de conjuntos múltiples organizados en orden no descendente, podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
//Template definition template <typename T> //Function to print multiset elements // arranged in non-descending order void print(multiset<T>& a) { cerr << '[' << ' '; for (auto x : a) { // Same as variable using the // concept of function overloading print(x); cerr << ' '; } cerr << ']'; }
- Para imprimir elementos de conjuntos múltiples , dispuestos en orden no ascendente, podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
//Template definition template <typename T> //Function to print elements of a // multiset arranged in non-ascending order void print(multiset<T, greater<T> >& a) { cerr << '[' << ' '; for (auto x : a) { // Same as variable using the // concept of function overloading print(x); cerr << ' '; } cerr << ']'; }
- Para imprimir elementos de conjuntos desordenados , podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
// Template definition template <typename T> // Print function to print unordered // set elements void print(unordered_set<T>& a) { cerr << '[' << ' '; for (auto x : a) { // Same as variable using the // concept of function overloading print(x); cerr << ' '; } cerr << ']'; }
- Para imprimir elementos vectoriales de vectores , podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
// Template definition template <typename T> // Function to print vector of // vectors elements void print(vector<vector<T> >& a) { cerr << "[ "; for (auto i : a) { // Same as variable using the // concept of function overloading print(i); cerr << " "; } cerr << "]"; }
- Para imprimir un par de elementos, podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
// Template definition template <typename T, typename V> // Function to print elements of a pair void print(pair<T, V> x) { // Sam as printing the variable using // the concept of function overloading print(x.ff); cerr << ':'; // Same as variable using the concept // of function overloading print(x.ss); }
- Para imprimir un par de elementos vectoriales, podemos crear una función de impresión con una definición de plantilla justo encima de la función:
C++
// Template definition template <typename T, typename V> // Function to print vector of // pairs elements void print(vector<pair<T, V> >& a) { cerr << '[' << ' '; for (auto x : a) { // Same as printing a variable using // the concept of function overloading print(x.ff); cerr << ":"; // Same as printing a variable using // the concept of function overloading print(x.ss); cerr << ' '; } cerr << ']'; }
Paso 2: cree un archivo nuevo más ( error.txt ) y asegúrese de que esté en la misma carpeta.
Archivo error.txt: todos los elementos de las estructuras de datos que hemos mencionado en los fragmentos anteriores se imprimirán en este archivo de texto solo sin afectar el archivo output.txt.
Tenga en cuenta que queremos escribir en este archivo usando flujo de error (cerr). Nuevamente, se puede tomar la ayuda de las directivas de preprocesador ifndef y endif .
C++
// We want to skip writing on error.txt // file when online judge (Codechef, // Codeforces, etc) is defined #ifndef ONLINE_JUDGE freopen("error.txt", "w", stderr); // ONLINE_JUDGE #endif
Después de agregar las líneas anteriores en nuestra función local, la función completa se convierte en:
C++
// Now local function would look like: void local() { #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); // ONLINE_JUDGE #endif // It basically means that these // statements (Between ifndef and // endif) would be skipped / ignored // if ONLINE_JUDGE is defined We don't // need to comment "local();" statement // in our function while submitting our // source code file to online judges. // It would be handled automatically #ifndef ONLINE_JUDGE freopen("error.txt", "w", stderr); // ONLINE_JUDGE #endif }
Paso 3: Además, no queremos comentar las declaraciones de depuración (estructura_datos) al enviar el archivo de código fuente a los jueces en línea. En palabras simples, existe la necesidad de encontrar una manera de que las funciones de depuración funcionen para el texto sublime (IDE) pero serían omitidas/ignoradas por los jueces en línea.
Nuevamente, esto se puede lograr usando las directivas de preprocesador ifndef y endif nuevamente en el código fuente.
C++
// If online judge is defined #ifndef ONLINE_JUDGE #define debug(x) cerr << #x << " "; print(x); cerr << '\n'; // If Online Judge is not defined #else #define debug(x) #endif
Ahora el IDE se vería similar a esto:
Paso 4: Siempre que exista la necesidad de verificar el estado de cualquier estructura de datos, se puede realizar la siguiente llamada:
// Calling from the main function debug(dataStructure);
Paso 5: A continuación se muestra la implementación del método anterior:
Ejemplo 1:
C++
/* It is recommended below snippets in your template file of competitive programming */ #include <bits/stdc++.h> using namespace std; // Debugging Functions template<class T>void print(T x) { cerr << x; } template<class T, class V> void print(pair<T , V> x) { print(x.ff); cerr << ':'; print(x.ss); } template<class T> void print(vector<T> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(set<T> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(set<T, greater<T>> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(multiset<T> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(multiset<T, greater<T>> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(unordered_set<T> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T, class V> void print(vector<pair<T, V>> &a) { cerr << '[' << ' '; for(auto x : a) { print(x.ff); cerr << ":"; print(x.ss); cerr << ' '; } cerr << ']'; } template <class T, class V> void print(map <T, V> &a) { cerr << "[ "; for (auto i : a) { print(i); cerr << " "; } cerr << "]"; } template <class T, class V> void print(unordered_map <T, V> &a) { cerr << "[ "; for (auto i : a) { print(i); cerr << " "; } cerr << "]"; } template <class T> void print(vector<vector<T>> &a) { cerr << "[ "; for (auto i : a) { print(i); cerr << " "; } cerr << "]"; } void local() { // ONLINE_JUDGE #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif // ONLINE_JUDGE #ifndef ONLINE_JUDGE freopen("error.txt", "w", stderr); #endif #ifndef ONLINE_JUDGE #define debug(x) cerr << #x << " "; print(x); cerr << '\n'; #else #define debug(x) #endif } // Driver code int main() { local(); // Number of elements in the vector int n; // Taking input from the user // through input.txt file cin >> n; // Declaring a vector of integer // type of size n vector<int> vect1(n); // Initializing the vector for(int i = 0 ; i < n ; i++) cin >> vect1[i]; // Modifying the vector for (auto& x : vect1) { if (x % 2 == 0) x += 10; else x -= 10; } // Printing vect1 elements // It will be printed in error.txt // file using cerr stream debug(vect1); // Initializing a vector of string type vector<string> vect2 = {"Geeks", "for", "Geeks"}; // Printing vect2 elements // It will be printed in error.txt // file using cerr stream debug(vect2); // Modifying vect2 // push back string "is great" vect2.push_back("is the great"); // Printing vect2 after modification // It will be printed in error.txt // file using cerr stream debug(vect2); // Calculating the answer int finalAnswer = 0; for (auto x : vect1) finalAnswer += x; // Finally, printing answer in output.txt // file using cout stream cout << "Final Answer: " << finalAnswer; return 0; }
archivo.cpp archivo:
archivo de entrada.txt
archivo de salida.txt
archivo error.txt
Ejemplo 2:
C++
/* It is recommended below snippets in your template file of competitive programming */ #include <bits/stdc++.h> using namespace std; // Debugging Functions template<class T>void print(T x) { cerr << x; } template<class T , class V> void print(pair<T, V> x) { print(x.ff); cerr << ':'; print(x.ss); } template<class T> void print(vector<T> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(set<T> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(set<T, greater<T>> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(multiset<T> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(multiset<T, greater<T>> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T> void print(unordered_set<T> &a) { cerr << '[' << ' '; for(auto x : a) { print(x); cerr << ' '; } cerr << ']'; } template<class T, class V> void print(vector<pair<T, V>> &a) { cerr << '[' << ' '; for(auto x : a) { print(x.ff); cerr << ":"; print(x.ss); cerr << ' '; } cerr << ']'; } template <class T, class V> void print(map <T, V> &a) { cerr << "[ "; for (auto i : a) { print(i); cerr << " "; } cerr << "]"; } template <class T, class V> void print(unordered_map <T, V> &a) { cerr << "[ "; for (auto i : a) { print(i); cerr << " "; } cerr << "]"; } template <class T> void print(vector<vector<T>> &a) { cerr << "[ "; for (auto i : a) { print(i); cerr << " "; } cerr << "]"; } void local() { // ONLINE_JUDGE #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif // ONLINE_JUDGE #ifndef ONLINE_JUDGE freopen("error.txt", "w", stderr); #endif #ifndef ONLINE_JUDGE #define debug(x) cerr << #x << " "; print(x); cerr << '\n'; #else #define debug(x) #endif } // Driver code int main() { local(); // Number of elements in the set int n; // Taking input from the user // through input.txt file cin >> n; // Declaring a set of integers set<int> set1; for(int i = 0 ; i < n ; i++) { int number; // Taking input from the user // through input.txt file cin >> number; //Inserting in the set set1.insert(number); } // Erasing from the set if(!set1.empty()) { // erasing the first element // of the set set1.erase(set1.begin()); } // Printing set1 elements // It will be printed in error.txt // file using cerr stream debug(set1); // Declaring another set set<string> set2; // Inserting in the set set2.insert("GeeksforGeeks"); // Printing set2 elements // It will be printed in error.txt file // using cerr stream debug(set2); // Inserting in set // Insert the string "is great" set2.insert("Geek"); // Printing set2 elements after // inserting into the set, It will // be printed in error.txt file // using cerr stream debug(set2); // Calculating the answer int finalAnswer = 0; for (auto x : set1) finalAnswer += x; // Finally, printing answer in output.txt // file using cout stream cout << "Final Answer: " << finalAnswer; return 0; }
archivo.cpp archivo:
archivo de entrada.txt
archivo de salida.txt
archivo error.txt
Ventaja de este método de depuración:
- Ahora no es necesario comentar cada una de las declaraciones de depuración en el programa antes de enviar el archivo de código fuente a los jueces en línea.
- La estructura de datos o los elementos STL se imprimirían en un archivo separado (error.txt) y los valores de salida deseados se imprimirían en el archivo output.txt, haciéndolo más legible.
- En pocas palabras, esto puede ahorrar mucho tiempo durante un concurso de codificación.