Dans une interview récente, on m'a posé une question vraiment étrange. L'intervieweur m'a demandé comment puis-je calculer 1 + 2 + 3 + ... + 1000 simplement en utilisant les fonctionnalités du compilateur. Cela signifie que je ne suis pas autorisé à écrire un programme et à l'exécuter, mais je devrais simplement écrire un programme qui pourrait conduire le compilateur à calculer cette somme pendant la compilation et imprimer le résultat lorsque la compilation est terminée. À titre indicatif, il m'a dit que je pourrais utiliser les fonctionnalités génériques et pré-processeur du compilateur. Il est possible d'utiliser un compilateur C ++, C # ou Java. Des idées???
Cette question n'est pas liée au calcul de la somme sans aucune boucle posée ici . En outre, il convient de noter que la somme DEVRAIT être calculée lors de la compilation. Imprimer uniquement le résultat à l'aide des directives du compilateur C ++ n'est pas acceptable.
En lisant plus sur les réponses publiées, j'ai trouvé que la résolution de problèmes lors de la compilation à l'aide de modèles C ++ s'appelle une métaprogrammation . C'est une technique qui a été découverte accidentellement par le Dr Erwin Unruh, lors du processus de normalisation du langage C ++. Vous pouvez en savoir plus sur ce sujet sur la page wiki de la méta-programmation . Il semble qu'il soit possible d'écrire le programme en Java en utilisant des annotations java. Vous pouvez jeter un oeil à la réponse de maress ci-dessous.
Un joli livre sur la méta-programmation en C ++ est celui-ci . Cela vaut la peine de jeter un coup d'œil si vous êtes intéressé.
Une bibliothèque de méta-programmation C ++ utile est le MPL de Boost ce lien .
const int value = 1 + 2 + 3.... + 1000; Console.WriteLine(value);
; PRéponses:
Mis à jour maintenant avec une profondeur de récursion améliorée! Fonctionne sur MSVC10 et GCC sans profondeur accrue. :)
Récurrence simple à la compilation + ajout:
Testcode:
Sortie pour GCC:
Exemple en direct sur Ideone .
Sortie pour MSVC10:
la source
Exemple C # d'erreur au moment de la compilation.
Produit l'erreur de compilation suivante:
la source
Une astuce populaire pour imprimer un nombre lors de la compilation consiste à essayer d'accéder à un membre inexistant d'un modèle instancié avec le numéro que vous souhaitez imprimer:
Le compilateur dit alors:
Pour un exemple plus intéressant de cette technique, consultez Résoudre le problème des huit reines au moment de la compilation .
la source
print_n
rester indéfini, voir ma réponse.Puisque ni le compilateur ni le langage n'ont été spécifiés dans la question de l'entretien, j'ose soumettre une solution dans Haskell en utilisant GHC:
Compilez-le:
Et nous avons également un programme de travail.
la source
La vie sera beaucoup plus facile avec C ++ 11 qui ajoute des
constexpr
fonctions pour le calcul du temps de compilation, bien qu'elles ne soient actuellement prises en charge que par gcc 4.6 ou version ultérieure.La norme exige uniquement que le compilateur prenne en charge une profondeur de récursivité de 512, il doit donc toujours éviter la profondeur de récursivité linéaire. Voici le résultat:
Bien sûr, vous pouvez simplement utiliser la formule:
la source
constexpr
pendant un moment. Peut-être que j'aime trop les modèles. :(/ 2
à gérer la gamme complète desunsigned
résultats possibles , la valeur que vous déplacez à droite devrait être de n + 1 bits de large, mais ce n'est pas le cas. Il est possible de réorganiser la formule pour éviter cela, comme le fait clang pour les plages de variables d'exécution: godbolt.org/z/dUGXqg montre que clang connaît la formule de forme fermée et l'utilise pour optimiser lestotal += i
boucles.En java, j'ai pensé à utiliser le traitement des annotations. L'outil apt analyse le fichier source avant d'analyser le fichier source avec la commande javac.
Lors de la compilation des fichiers source, la sortie sera imprimée:
L'usine de processeurs:
Le processeur d'annotations actuel:
Ensuite, nous créons un fichier source. classe simple qui utilise l'annotation MyInterface:
Le processeur d'annotations est compilé dans un fichier jar, puis l'outil apt est utilisé pour compiler le fichier source comme:
La sortie du projet:
la source
Voici une implémentation qui fonctionne sous VC ++ 2010. J'ai dû diviser les calculs en 3 étapes puisque le compilateur s'est plaint lorsque les modèles ont récidivé plus de 500 fois.
Lorsque vous compilez ceci, vous devriez voir cette sortie du compilateur quelque chose comme ceci:
la source
Je me sens obligé de donner ce code C, puisque personne d'autre ne l'a encore:
Et il ne me reste plus qu'à vérifier l'assemblage pour trouver ma réponse!
Et je vois:
la source
x
était global, le compilateur serait (plus ou moins) nécessaire pour évaluer l'expression au moment de la compilation. ISO C n'autorise pas les initialiseurs de variables d'exécution pour les globaux. Bien sûr, une implémentation spécifique peut émettre un appel à une fonction static-init de type constructeur qui la calcule lors de l'exécution et stocke. Mais ISO C vous permet d'utiliser des constantes de temps de compilation comme tailles de tableau (commeint y[x];
dans une définition de struct ou comme un autre global par exemple), donc toute implémentation hypothétique de pessimisation devrait toujours prendre en charge cela.Extension de la réponse de Carl Walsh pour imprimer le résultat lors de la compilation:
sorties gcc:
la source
Vous pouvez utiliser (et surtout abuser) des macros / modèles C ++ pour faire de la métaprogrammation . AFAIK, Java ne permet pas le même genre de chose.
la source
En théorie, vous pouvez utiliser ceci:
(basé sur le code publié par Xeo). Mais GCC me donne cette erreur:
plus un énorme pseudo-stacktrace.
la source
En utilisant java, vous pouvez faire une chose similaire à la réponse C #:
vous pouvez le faire dans scala en utilisant des nombres peano car vous pouvez forcer le compilateur à faire de la récursivité mais je ne pense pas que vous puissiez faire la même chose en c # / java
une autre solution n'utilisant pas -Xprint mais encore plus douteuse
sans utiliser d'indicateur de compilation. puisque vous pouvez rechercher un nombre arbitraire de constantes (pas seulement 500500), cette solution devrait être acceptable.
la source
500500
, désolé.for (i = 0; i < 100000; ++i) {if (i == 1000*1000/2) print i}
. J'ai un fichier java de 160 Mo qui fait cela et cela fonctionne :)Bien que cela fonctionne réellement avec de petits nombres, clang ++ me renvoie une erreur de compilation si j'utilise sum_first où N> 400.
la source