Tipps zum Golfen in C ++

48

Welche allgemeinen Tipps haben Sie zum Golfen in C ++? Ich bin auf der Suche nach Ideen, die sich auf Code-Golf-Probleme im Allgemeinen anwenden lassen, die zumindest etwas spezifisch für C ++ sind (z. B. "Kommentare entfernen" ist keine Antwort). Bitte posten Sie einen Tipp pro Antwort.

marcog
quelle
4
Viele der Tipps zum Golfen in C gelten auch für C ++. Gehen Sie daher davon aus, dass die Leser mit dieser Frage vertraut sind. Schreiben Sie hier nur, wenn Sie etwas haben, das auch kein gültiger C-Golftipp ist.
Toby Speight
@TobySpeight Wahrscheinlich, weil sie neben der Fragen-ID dieselbe URL haben.
NoOneIsHere
C und C ++ sind richtig und einfach (wenn man die richtige Teilmenge von C ++ betrachtet)
RosLuP

Antworten:

24

Der ternäre Bedingungsoperator ?:kann oft als in für einfachen Stand verwendet werden if- elseAussagen zu erheblichen Einsparungen.

Dies ist insofern von besonderem Wert, als damit alternative Werte wie in ausgewählt werden können

#include <iostream>
#include <cstdlib>
int main(int c, char**v){
  int o=0,e=0,u;
  while(--c) ((u=atoi(v[c]))%2?o:e)+=u;
  std::cout << "Sum of odds " << o <<std::endl
            << "Sum of evens " << e <<std::endl;
}
dmckee
quelle
Ich habe den Code noch nicht ausgeführt, aber ich glaube nicht, dass er so funktioniert, wie Sie es sagen. ((u = atoi (v [c]))% 2? o: e) + = u addiert nur den Wert u zum Ausdruck auf der linken Seite, der den Wert o oder e erhält, aber die Variablen o und e bleiben unverändert, so dass sie immer 0 sind. Überprüfen Sie den Code, um zu sehen, was passiert. Sie sollten Adressen verwenden, damit es funktioniert
Bogdan Alexandru
4
@ BogdanAlexandru Er ... mach es. Es funktioniert wirklich. Der Wert des Klammerausdrucks ist ein Verweis auf das eine oder andere von eund o. Beachten Sie, dass sich dies von der Funktionsweise dieses Operators in c unterscheidet, in dem dieser Trick nicht funktioniert, da er kein Wert sein kann.
dmckee
Ersetzen Sie std::endlmit '\n'dem spart 5 Zeichen
Mukul Kumar
3
@ MukulKumar Nun ja. Aber um diesen Tipp zu demonstrieren, ließ ich alles außer dem Ternär-Bedingten aus Gründen der Klarheit ungolfen.
dmckee
22

Manchmal können Sie zwei Zeichen speichern, indem Sie die Tatsache verwenden, dass statische Speicherdauervariablen (die insbesondere alle globalen Bereichsvariablen umfassen) zu Beginn automatisch auf Null gesetzt werden (im Gegensatz zu automatischen Variablen, für die Sie keine solche Garantie haben). Also statt

int main()
{
  int a=0;
  // ...
}

Du kannst schreiben

int a;
int main()
{
  // ...
}
Celtschk
quelle
+1 aber definitiv schlechte Praxis
Mondlos
@mondlos: Golfen impliziert grundsätzlich eine schlechte Praxis.
Celtschk
15

Einige Compiler (zB GCC) unterstützen Konstanten mit mehreren Zeichen . Dies kann einige Zeichen sparen, wenn ein großer ganzzahliger Wert erforderlich ist. Beispiel:

int n='  ';

Der Wert ist implementierungsspezifisch. In der Regel der Wert 'ab'ist 256*'a'+'b'oder 'a'+256*'b'. Sie können bis zu 4 Zeichen in Anführungszeichen setzen.

marcog
quelle
3
GCC? Du meinst g ++ ?
Nathan Osman
6
@ George Edison: GCC steht für die GNU Compiler Collection , die alle Frontends umfasst, einschließlich derer für C, C ++, Go usw.
Joey Adams
@ Joey: Ich weiß, aber es ist auch der Name des GNU C Compilers.
Nathan Osman
25
@ George: Der GNU C-Compiler heißt gcc, nicht GCC.
Fredoverflow
Könnte mich auch daran erinnern, ich könnte es vergessen.
12

Eines, das ich als nützlich empfand:

Unter Ausnutzung der Tatsache , dass Nicht-Null - Werte zu bewerten truein Booleschen Ausdrücken, und dass x&&yauswertet , um , x*ywenn sie mit booleans Umgang

(x!=0 && y!=0)

bewertet zu

(x*y)

Sie müssen sich nur der Überläufe bewusst sein, wie unten ausgeführt.

Baldrickk
quelle
2
Technisch ist es x!=0 && y!=0. Bei der Verwendung der Multiplikation müssen Sie jedoch vorsichtig mit Überläufen umgehen. Bei Verwendung von 32-Bit-Ganzzahlen würde x = y = 65536 (und mehrere andere Kombinationen von Zweierpotenzen) ebenfalls x * y = 0 ergeben .
Martin Ender
Ja, das ist richtig. Ich habe es als zweidimensionales Array verwendet, um die Grenzen hier zu überprüfen: codegolf.stackexchange.com/a/37571/31477, wo das keine Rolle spielte. Ich bearbeite diese Punkte in.
Baldrickk
1
Beachten Sie jedoch, dass &&ein Kurzschlussverhalten *fehlt. Zum Beispiel können Sie nicht ersetzen i++!=0&&j++!=0mit i++*j++.
Celtschk
@ Celtschk ja, guter Punkt. Aber wenn Sie nur die Boolesche Algebra machen, dann funktioniert es
Baldrickk
11

Verwenden Sie die folgenden Typen:

u64, s64, u32, s32 (or int)

Verwenden Sie für sich wiederholende Wörter / Typen #defines:

#define a while

Es lohnt sich nur, wenn Sie whileviel verwenden, um die zusätzlichen 10 Zeichen auszugleichen. ( Ungefähr 4. )

Mateen Ulhaq
quelle
1
Die Typen u64, s64, u32 und s32 sind nicht Bestandteil von C ++. Sie sind möglicherweise eine nicht standardmäßige Erweiterung Ihres Compilers (ich habe sie jedoch noch nie gesehen).
Celtschk
5
Diese beiden Tipps sollten besser in zwei getrennten Antworten platziert werden, damit über sie einzeln abgestimmt werden kann.
Trichoplax
11

Wenn Sie bereit sind, C ++ 0x zu verwenden, können Sie neue Funktionen wie Lambdas verwenden .

Mechanische Schnecke
quelle
10

Wenn möglich, ändern &&und ||zu &und |ist.

Bei Verwendung von einfachen if-Anweisungen:

if(<condition>)<stuff>;

kann geändert werden in:

<condition>?<stuff>:<any single letter variable>;

was einen Charakter speichert.

Alex Gittemeier
quelle
8

Anstatt zu benutzen while(1), benutze for(;;), speichere ein Zeichen :)

NaCl
quelle
8

Die Verwendung des Komma-Operators anstelle von öffnenden und schließenden Klammern kann einige Zeichen sparen, wenn Ihre Klauseln mehr als eine Anweisung enthalten:

if(c){x=1;cout<<"Hi";y=2;}else{x=2;cout<<"Bye";y=3;}

gegen

if(c)x=1,cout<<"Hi",y=2;else x=2,cout<<"Bye",y=3;###

Zwei Zeichen auf einer einfachen IF oder drei insgesamt für eine IF / ELSE gespeichert.

Zur Unterscheidung zwischen C und C ++ kann das Ergebnis eines Kommaausdrucks in C ++ als Ganzes als Wert ... FWIW verwendet werden.

Dr. Rebmu
quelle
7

Da Array-Elemente direkt hintereinander im Speicher abgelegt werden, anstatt wie folgt:

for(int x = 0; x < 25; x++) {
    for(int y = 0; y < 25; y++)
        array[x][y] = whatever;
}

Sie können so etwas tun:

int* pointer = array;
for(int i = 0; i < 25*25; i++, pointer++)
    *pointer = whatever;

Aus Gründen der Lesbarkeit ist keines der oben genannten Elemente Golf. Die explizite Verwendung von Zeigern kann jedoch viel Platz einsparen.

Stuntddude
quelle
Vergiss nicht, dass du das ganze Leerzeichen ausschneiden kannst! (
Anderer
@stokastic Die Beispiele sind nicht zum Golfen gedacht, sondern nur zur Veranschaulichung der Verwendung der Technik.
Stuntddude
6
warum nicht for(int* i=array; i<array+25*25; i++)? Dann müssen Sie nur noch eine Variable im Auge behalten.
Lucas
6

Es liegt auf der Hand, aber wenn Sie einen Großteil der Standardbibliothek verwenden, using namespace std;können einige Zeichen gespeichert werden.

Developerbmw
quelle
5
Wenn Sie nur einen Namen verwenden, kann dieser jedoch häufig using std::name;kürzer sein.
Celtschk
10
Dies speichert Zeichen nur, wenn Sie std::fünf oder mehr Mal verwenden.
nyuszika7h
6

Es ist nützlich, sich daran zu erinnern, dass dies a[i]dasselbe ist wie *(a+i).

Ersetzen Sie a[0]durch, *aum zwei Zeichen zu sparen. Auch a[i][0]ist gleichbedeutend mit *a[i]und a[0][i]schrumpft auf i[*a]. Wenn Sie also einen 0Index in Ihrem Array fest codieren , gibt es wahrscheinlich einen besseren Weg.

MegaTom
quelle
5

Anstatt große Zehnerpotenzen zu schreiben, verwenden Sie die e-Notation . Zum Beispiel a=1000000000ist länger als a=1e9. Dies kann auf andere Zahlen erweitert werden, wie a=1e9+24es besser ist als a=1000000024.

Pranjal Jain
quelle
1
Beachten Sie, dass dies nicht genau gleichbedeutend ist und vor der Verwendung in Ganzzahltypen umgewandelt werden muss. Zum Beispiel 1e9/xist nicht dasselbe wie 1000000000/xoder int(1e9)/x.
user202729
5

Sie können den ternären Operator ?:ohne Ausdrücke im True-Block verwenden (es wird ein Byte gespeichert).

#include <iostream>

int foo()
{
    std::cout << "Foo\n";
}

int main()
{
    1?foo():0;  // if (true) foo()
    0?:foo();   // if (!false) foo()
}

Überprüfen Sie es hier

x1Mike7x
quelle
5
Dies scheint eine GNU-Erweiterung zu sein und nicht im C ++ - Standard. https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Conditionals.html#Conditionals
ceilingcat
r? foo (): 0; // wenn (r) foo () ist das ok ;;;;; aber dafür r?: foo (); Ich weiß es nicht
RosLuP
5

Kürzere Kopfzeile

Dies ist GCC-spezifisch und möglicherweise auf andere Compiler erweiterbar.

Vorkompilierter Header.

In G ++ bits/stdc++.hbesteht der vorkompilierte Header aus allen anderen Headern. Wenn Sie importzwei verschiedene benötigen, können Sie diese einfach verwenden.

Kürzere Kopfzeile.

Dies sind alle unter http://en.cppreference.com/w/cpp/header aufgelisteten Überschriften :

in aufsteigender Reihenfolge der Länge sortiert.

Einige von ihnen sind bereits länger als bits/stdc++.h, und einige von ihnen erfordern C ++ 17-Unterstützung. Einige andere werden von TIO G ++ nicht unterstützt (aus mir unbekannten Gründen). Filtern Sie sie heraus, die wir haben:

Es kann vorkommen, dass einige von ihnen durch kürzere ersetzt werden können. Nur binäre Suche, ob das, was Sie brauchen, ersetzt werden kann. Speziell:

cstdio -> ios        (-3 bytes)
algorithm -> regex   (-4 bytes)
vector -> queue      (-1 byte)
string -> map        (-3 bytes)
bitset -> regex      (-1 byte)
numeric -> random    (-1 byte)
user202729
quelle
4

#importanstatt #includedir ein weiteres Byte zu geben.

Außerdem muss das Leerzeichen zwischen #importund header nicht unbedingt sein:

#include <map>
// vs
#import<map>

Und wenn Sie etwas aus dem stdlibHeader benötigen , können Sie jeden Header mit STL-Container (vorzuziehen setoder map) anstelle von importieren cstdlib.

x1Mike7x
quelle
3

Arithmetische Operationen mit Booleschen Werten:

Obwohl

a*=b>0?.5:-.5

ist besser als

if(b>0)a*=.5;else a*=-.5;

es ist nicht so gut wie

a*=(b>0)-.5

Verwenden Sie #define auch für alles, was häufig verwendet wird. Sie ist oft kürzer als die Verwendung von Funktionen, da Typnamen nicht erforderlich sind.

Kombiniere die Dinge so oft wie möglich:

a+=a--;

ist das gleiche wie

a=2*a-1;
Lucas
quelle
Während Ihre Beispiele korrekt sind, sollten Sie darauf achten, undefiniertes Verhalten aufzurufen, wenn Sie xlvalue und x++rvalue verwenden. undefiniertes Verhalten und Sequenzpunkte
Ceilingcat
Ja möglich a + = a--; hat
Undefiniertes
3

Verwenden Sie generische Lambdas als billige Vorlagen

Bei anderen Typen intkann die Verwendung als Funktionsargumente teuer sein. Es wurden jedoch generische Lambdas eingeführt (in C ++ 14?), Die es jedem Lambda erlauben, eine Vorlage zu sein - mit autoden Argumenttypen können Bytes gespart werden. Vergleichen Sie:

double f(double x, double y)
[](auto x, auto y)

Generische Lambdas sind auch sehr praktisch, um Iteratoren zu akzeptieren - wahrscheinlich ist die beste Methode, um Array-Eingaben in C ++ zu akzeptieren [](auto a, auto z), wo aund zwie begin()und end()vom Array / vector / list / etc übergeben werden.

Toby Speight
quelle
2

Bei meinem ersten Versuch, Golf für die Aufgabe "Subtrahiere die nächsten Zahlen" zu codieren, bin ich von der Funktion ausgegangen (58 Bytes)

int f(int N, int P){int F;for(F=N;P;F-=++N,P--);return F;}

dann sichere 5 Bytes mit Verschiebung nach Lambda und Verschieben der Initialisierung aus for(53)

[](int N,int P){int F=N;for(;P;F-=++N,P--);return F;}

und schließlich nach dem Wechsel von forzu whilebekam ich 51 Bytes:

[](int N,int P){int F=N;while(P--)F-=++N;return F;}

Der ungolfed Testcode ist so etwas wie:

#include <iostream>
int main(void)
{
    int N, P;
    std::cin >> N >> P;
    auto f = [](int N,int P)
    {
        int F = N;
        while (P--)
            F -= ++N;
        return F;
    };
    std::cout << f(N, P) << std::endl;
    return 0;
}

AKTUALISIEREN:

forKann tatsächlich die gleiche Länge erreichen wie while:

[](int N,int P){int F=N;for(;P--;F-=++N);return F;}
VolAnd
quelle
2

Irgendwann zu spät zur Party, denke ich ...

Wenn Sie einen Ausdruck in -1 und 1 anstelle von 0 und 1 umwandeln möchten, gehen Sie stattdessen wie folgt vor:

int x;
if (a * 10 > 5)
    x = 1;
else
    x = -1;

mach das:

int x = (a * 10 > 5) * 2 - 1;

Je nach Verwendung können einige Bytes eingespart werden.

Yuval Meshorer
quelle
Könnten int x=(a*10>5)*2-1;Sie das nicht int x=a*10>5?1:-1;, was 1 Byte kürzer ist?
Girobuz
2

Wenn Sie zwei ganzzahlige Variablen a und b vertauschen möchten,

a^=b^=a^=b;

kann verwendet werden und 5 Zeichen als die Standardmethode speichern

a+=b;
b=a-b;
a-=b;
joker007
quelle
1
Über diesen Standard. ,tbei den zuvor erstellten ints t=a;a=b;b=t;wäre das dann schon 3 bytes kürzer gewesen als beim a+=b;b=a-b;a-=b;. Trotzdem ist deine a^=b^=a^=b;noch kürzer, also +1 von mir. Ich kenne C ++ nicht, aber es funktioniert tatsächlich . Als Java-Code-Golfer bin ich traurig, dass es dort anscheinend nicht funktioniert . :(
Kevin Cruijssen
1
@ KevinCruijssen Ja, ich hätte C ++ erwähnen sollen, ich kenne Java nicht viel, aber a^=b;b^=a;a^=b;es funktioniert gut in Java.
Joker007
1
C ++ muss nicht explizit erwähnt werden. Alle diese Tipps gelten für C ++. :) Als Java-Entwickler war ich nur neugierig, ob so etwas in Java möglich ist, aber anscheinend nicht. a^=b;b^=a;a^=b;funktioniert zwar, ist aber länger als das ,t+ t=a;a=b;b=t;. Es tut mir leid, Java erwähnt zu haben, da es hier nicht zum Thema gehört. Aber netter Tipp für C ++ Codegolfspieler!
Kevin Cruijssen
2

Verwenden Sie GCC-Builtins, anstatt sie zu importieren

Wenn Sie einen GCC-Compiler verwenden, ist es manchmal hilfreich, die darin enthaltenen Funktionen wie __builtin_putsoder zu verwenden __builtin_clz. Zum Beispiel,

44 Bytes:

int main(){__builtin_puts("Hello, world!");}`

50 Bytes:

#import<cstdio>
int main(){puts("Hello, world!");}
Dingledooper
quelle
1

Wenn Sie C ++ 11 oder neuer verwenden (was jetzt immer der Fall sein sollte), verwenden Sie auto Option nach Möglichkeit für komplexe Typen.

Beispiel: 54 Bytes statt 66

#include<vector>
std::vector<int> f(std::vector<int> l){return l;}
#include<vector>
auto f(std::vector<int> l){return l;}

Da die Leistung keine Rolle spielt, kann es bei einigen Herausforderungen auch std::listsein, dass der Job nur um ein paar Bytes weniger erledigt wird :

#include<list>
auto f(std::list<int> l){return l;}
movatica
quelle
1

Funktionen <algorithm>erfordert oft beiläufig a.begin(),a.end()die wirklich lang ist, stattdessen können Sie mit &a[0],&*end(a)3 Bytes speichern , wenn aist vectoroder string.

sort(a.begin(),a.end());
sort(begin(a),end(a));
sort(&a[0],&*end(a));
JayXon
quelle
0

Nicht benutzen string(""), benutzen "". Das spart 8 Bytes.

Rɪᴋᴇʀ
quelle
Es ist nicht genau gleichbedeutend. Zum Beispiel "" + 'a'ist char* + char, die Zeiger hinaus ist, während std::string("") + 'a'ist std::string + char- String - Verkettung. string()würde funktionieren.
user202729