In einem kürzlich geführten Interview wurde mir eine wirklich seltsame Frage gestellt. Der Interviewer fragte mich, wie ich 1 + 2 + 3 + ... + 1000 nur mit Compiler-Funktionen berechnen kann. Dies bedeutet, dass ich kein Programm schreiben und ausführen darf, sondern nur ein Programm schreiben sollte, das den Compiler dazu bringen könnte, diese Summe während der Kompilierung zu berechnen und das Ergebnis nach Abschluss der Kompilierung zu drucken. Als Hinweis sagte er mir, dass ich Generika und Vorprozessorfunktionen des Compilers verwenden könnte. Es ist möglich, C ++, C # oder Java-Compiler zu verwenden. Irgendwelche Ideen???
Diese Frage bezieht sich nicht auf die Berechnung der Summe ohne die hier gestellten Schleifen . Zusätzlich sollte beachtet werden, dass die Summe während der Kompilierung berechnet werden sollte. Es ist nicht akzeptabel, nur das Ergebnis mit C ++ - Compileranweisungen zu drucken.
Als ich mehr über die veröffentlichten Antworten las, stellte ich fest, dass das Lösen von Problemen beim Kompilieren mit C ++ - Vorlagen als Metaprogrammierung bezeichnet wird . Dies ist eine Technik, die Dr. Erwin Unruh versehentlich bei der Standardisierung der C ++ - Sprache entdeckt hat. Weitere Informationen zu diesem Thema finden Sie auf der Wiki-Seite der Metaprogrammierung . Es scheint möglich zu sein, das Programm mit Java-Annotationen in Java zu schreiben. Sie können sich die Antwort von Maress unten ansehen .
Ein schönes Buch über Metaprogrammierung in C ++ ist dieses . Bei Interesse einen Blick wert.
Eine nützliche C ++ - Metaprogrammierbibliothek ist Boosts MPL dieser Link .
const int value = 1 + 2 + 3.... + 1000; Console.WriteLine(value);
; PAntworten:
Jetzt mit verbesserter Rekursionstiefe aktualisiert ! Funktioniert mit MSVC10 und GCC ohne größere Tiefe. :) :)
Einfache Rekursion zur Kompilierungszeit + Addition:
Testcode:
Ausgabe für GCC:
Live-Beispiel auf Ideone .
Ausgabe für MSVC10:
quelle
C # -Beispiel zum Fehler beim Kompilieren.
Erzeugt den folgenden Kompilierungsfehler:
quelle
Ein beliebter Trick zum Drucken einer Nummer während der Kompilierung ist der Versuch, auf ein nicht vorhandenes Mitglied einer Vorlage zuzugreifen, die mit der zu druckenden Nummer instanziiert ist:
Der Compiler sagt dann:
Ein interessanteres Beispiel für diese Technik finden Sie unter Lösen des Problems mit acht Königinnen zur Kompilierungszeit .
quelle
print_n
undefiniert bleiben lassen , siehe meine Antwort.Da in der Interviewfrage weder Compiler noch Sprache angegeben wurden, wage ich es, in Haskell eine Lösung mit GHC einzureichen:
Kompilieren Sie es:
Und wir haben auch ein Arbeitsprogramm.
quelle
Mit C ++ 11, das
constexpr
Funktionen für die Berechnung der Kompilierungszeit hinzufügt, wird das Leben viel einfacher , obwohl sie derzeit nur von gcc 4.6 oder höher unterstützt werden.Der Standard verlangt nur, dass der Compiler eine Rekursionstiefe von 512 unterstützt, sodass die lineare Rekursionstiefe weiterhin vermieden werden muss. Hier ist die Ausgabe:
Natürlich können Sie einfach die Formel verwenden:
quelle
constexpr
für einen Moment total vergessen . Vielleicht liebe ich Vorlagen einfach zu sehr. :(/ 2
darin, den gesamten Bereich möglicherunsigned
Ergebnisse zu verarbeiten. Der Wert, den Sie nach rechts verschieben, muss n + 1 Bit breit sein, ist es aber nicht. Es ist möglich, die Formel neu anzuordnen , um dies zu vermeiden, wie dies bei clang für Bereiche mit Laufzeitvariablen der Fall ist : godbolt.org/z/dUGXqg zeigt, dass clang die Formel in geschlossener Form kennt und sie zur Optimierung vontotal += i
Schleifen verwendet.In Java habe ich über die Verwendung der Annotationsverarbeitung nachgedacht. Das apt-Tool scannt die Quelldatei, bevor die Quelldatei tatsächlich mit dem Befehl javac analysiert wird.
Während der Kompilierung der Quelldateien wird die Ausgabe ausgedruckt:
Die Prozessorfabrik:
Der eigentliche Annotationsprozessor:
Dann erstellen wir eine Quelldatei. einfache Klasse, die MyInterface-Annotation verwendet:
Der Anmerkungsprozessor wird in eine JAR-Datei kompiliert. Anschließend wird das Quell-Tool mit dem apt-Tool wie folgt kompiliert:
Die Ausgabe des Projekts:
quelle
Hier ist eine Implementierung, die unter VC ++ 2010 funktioniert. Ich musste die Berechnungen in drei Phasen aufteilen, da sich der Compiler beschwerte, als die Vorlagen mehr als 500 Mal rekursiv waren.
Wenn Sie dies kompilieren, sollte diese Ausgabe des Compilers ungefähr so aussehen:
quelle
Ich fühle mich verpflichtet, diesen C-Code anzugeben, da noch niemand:
Und alles, was ich tun muss, ist die Baugruppe zu überprüfen, um meine Antwort zu finden!
Und ich verstehe:
quelle
x
global, müsste der Compiler (mehr oder weniger) den Ausdruck zur Kompilierungszeit auswerten. ISO C erlaubt keine Initialisierer von Laufzeitvariablen für Globals. Natürlich könnte eine bestimmte Implementierung einen Aufruf einer konstruktorähnlichen statischen Init-Funktion auslösen, die sie zur Laufzeit berechnet und speichert. Mit ISO C können Sie jedoch Kompilierungszeitkonstanten als Arraygrößen verwenden (wieint y[x];
z. B. in einer Strukturdefinition oder als eine andere globale), sodass jede hypothetische pessimisierende Implementierung dies weiterhin unterstützen müsste.Ausgehend von Carl Walshs Antwort, um das Ergebnis während der Kompilierung tatsächlich auszudrucken:
gcc-Ausgänge:
quelle
Sie können C ++ - Makros / Vorlagen verwenden (und meistens missbrauchen), um Metaprogrammierungen durchzuführen . AFAIK, Java erlaubt nicht das Gleiche.
quelle
Theoretisch können Sie dies verwenden:
(basierend auf dem Code, den Xeo gepostet hat). Aber GCC gibt mir diesen Fehler:
plus eine enorme Pseudo-Stacktrace.
quelle
Mit Java können Sie etwas Ähnliches wie mit der C # -Antwort tun:
Sie können dies in Scala mit Peano-Zahlen tun, weil Sie den Compiler zur Rekursion zwingen können, aber ich glaube nicht, dass Sie dasselbe in c # / java tun können
Eine andere Lösung, die -Xprint nicht verwendet, aber noch zwielichtiger ist
ohne Compiler-Flags zu verwenden. Da Sie nach einer beliebigen Anzahl von Konstanten suchen können (nicht nur nach 500500), sollte diese Lösung akzeptabel sein.
quelle
500500
, sorry.for (i = 0; i < 100000; ++i) {if (i == 1000*1000/2) print i}
. Ich habe eine 160 MB Java-Datei, die dies tut und es funktioniert :)Obwohl dies tatsächlich mit kleinen Zahlen funktioniert, gibt mir clang ++ einen Compilerfehler zurück, wenn ich sum_first verwende, wobei N> 400 ist.
quelle