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.


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;
}

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()
{
  // ...
}

+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.


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.


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. )


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


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.


8

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


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.


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.


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.


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.


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.


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


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)

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.


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;

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.


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;}

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.


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;

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!");}

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;}

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));

0

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


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.