Al examinar los protocolos de la entrevista para la posición del desarrollador, descubrí el siguiente problema: "Ofrecer un código que imprima números en orden decreciente de n a 0, sin usar operadores de comparación (ocultos o explícitos) (la implementación de la función de impresión no cuenta)". A pesar de que esta tarea no tenía nada que ver conmigo, me ocupó y decidí pensar en formas de resolverla (aunque todavía no sé quién y cuándo resolver qué tarea necesitaría este método de optimización de código).
Lo primero que me vino a la mente fue intentar usar patrones. Como asi
template<int n> void print() { printf("%d\n", n); print<n - 1>(); } template<> void print<0>() { printf("0\n"); } int main(int argc, char* argv[]) { print<N>(); return 0; }
El problema es que la programación es una disciplina de ingeniería, no una oculta, y si "algo" ocurriera en el sistema, entonces "esto" debería describirse y los recursos deberían asignarse, y en este caso, ya que el desarrollo de las plantillas tiene lugar en la etapa de compilación, hay restricciones en el anidamiento de tales construcciones (y es bueno y correcto, y gracias a Dios que lo es), y el compilador emitió con razón "error fatal C1202: tipo recursivo o contexto de dependencia de función demasiado complejo" con tamaño N mayor 2000.
Lo siguiente que me vino a la mente fue usar el método clásico con punteros a las funciones.
typedef void(*PrintFunc)(int); void printZero(int); void printNumber(int n); PrintFunc g_functors[] = {printZero, printNumber}; void printZero(int) { printf("0\n"); } void printNumber(int n) { printf("%d\n", n--); g_functors[!!n](n); } int main(int argc, char* argv[]) { printNumber(N); return 0; }
Pero incluso entonces, las restricciones que nos impone la ley de la naturaleza se hicieron sentir, y dado que la anidación de las llamadas a funciones está limitada por el tamaño de la pila, se obtuvo un "desbordamiento de pila" legítimo con un valor de N> 4630.
En este lugar, la decepción y la ira se apoderaron de mí por completo y me di cuenta de que para una solución completa a este problema, no debes evitar nada, incluidos los trucos más sucios. El problema es que para ciertos valores de N necesitamos transferir el control a las secciones necesarias del código. Y esto no es un problema cuando tenemos declaraciones condicionales a nuestra disposición, pero cuando no están allí, tenemos que recurrir a la brujería. En la antigüedad, esto se podía resolver usando el método goto, pero desde entonces los cazadores de brujas y otros luchadores de dragones han limitado severamente su funcionalidad (y esto también es bueno y correcto) y nos gustaría escribir algo así
g_functors[0] = [](int){print("0\n"); goto end; } g_functors[1] = [](int n){print("%d\n", n); goto begin; } begin: g_functors[!!n](n--); end:
pero no podemos
Por lo tanto, aunque se me prohibió participar en magia negra, en este caso decidí hacer una excepción agregando un poco de magia al ejemplo anterior.
void printZero(int); void printNumber(int n); PrintFunc g_functors[] = {printZero, printNumber}; void printZero(int) { printf("0\n"); throw 0; } void printNumber(int n) { printf("%d\n", n); } int main(int argc, char* argv[]) { int n = N; try{ begin: g_functors[!!n](n--); goto begin; } catch (...){} return 0; }
Y esto resolvió completamente el problema (para cualquier N).
PD: Estaré encantado de escuchar sobre otros métodos para resolver este problema.