Suggerimenti per giocare a golf in C ++


48

Quali consigli generali hai per giocare a golf in C ++? Sto cercando idee che possano essere applicate ai problemi del codice golf in generale che siano almeno in qualche modo specifiche per il C ++ (ad esempio "rimuovere i commenti" non è una risposta). Si prega di inviare un suggerimento per risposta.


4
Molti dei suggerimenti per giocare a golf in C sono applicabili anche al C ++, quindi supponiamo che i lettori abbiano familiarità con quella domanda; pubblica qui solo se hai qualcosa che non è anche un suggerimento valido per il golf C.
Toby Speight,

@TobySpeight Probabilmente perché hanno lo stesso URL oltre all'ID domanda.
NoOneIsHere

C e C ++, anche se non di tipo "golf", sono giusti e facili (se si considera il sottoinsieme giusto di C ++)
RosLuP

Risposte:


24

L'operatore condizionale ternario ?:spesso può essere utilizzato come uno stand in per semplice if- elsele dichiarazioni a notevoli risparmi.

Ha un valore speciale in quanto può essere utilizzato per selezionare valori alternativi come in

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

non ho ancora eseguito il codice, ma non penso che funzioni come dici tu. ((u = atoi (v [c]))% 2? o: e) + = u non fa altro che aggiungere il valore u all'espressione a sinistra che ottiene il valore oo e, ma le variabili oe e rimangono invariati quindi saranno sempre 0. controlla il codice per vedere cosa succede. dovresti usare gli indirizzi per farlo funzionare
Bogdan Alexandru,

4
@BogdanAlexandru Er ... eseguilo. Funziona davvero. Il valore dell'espressione tra parentesi è un riferimento all'uno o all'altro di ee o. Si noti che questo è diverso da come funziona questo operatore in c dove questo trucco non funziona perché non può essere un valore.
dmckee,

Sostituisci std::endlcon '\n'quello salva 5 caratteri
Mukul Kumar

3
@MukulKumar Bene, sì. Ma ai fini della dimostrazione di questo suggerimento ho lasciato tutto tranne il ternario-condizionato senza golf per chiarezza.
dmckee,

22

A volte è possibile salvare due caratteri utilizzando il fatto che le variabili di durata dell'archiviazione statica (che include in particolare tutte le variabili dell'ambito globale) vengono automaticamente inizializzate a zero all'inizio (diversamente dalle variabili automatiche in cui non si dispone di tale garanzia). Quindi invece di

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

tu puoi scrivere

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

+1 ma decisamente cattiva pratica
lunedì

@mondlos: il golf implica fondamentalmente cattive pratiche.
Celtschk,

15

Alcuni compilatori (ad es. GCC) supportano costanti multi-carattere . Questo può salvare alcuni caratteri quando è richiesto un valore intero grande. Esempio:

int n='  ';

Il valore è specifico dell'implementazione. Di solito il valore di 'ab'è 256*'a'+'b'o 'a'+256*'b'. È possibile specificare fino a 4 caratteri tra virgolette.


3
GCC? Intendi g ++ ?
Nathan Osman,

6
@George Edison: GCC sta per GNU Compiler Collection , che comprende tutti i suoi frontend, compresi quelli per C, C ++, Go, ecc.
Joey Adams,

@Joey: lo so, ma è anche il nome del compilatore GNU C.
Nathan Osman,

25
@George: il compilatore GNU C si chiama gcc, non GCC.
fredoverflow,

Potrei anche ricordare che, potrei dimenticare.

12

Uno che ho trovato utile:

Sfruttare il fatto che valori diversi da zero vengono valutati truein espressioni booleane e che x&&yvaluta x*yquando si tratta di valori booleani

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

valuta

(x*y)

Devi solo essere consapevole degli overflow, come indicato di seguito.


2
Tecnicamente lo è x!=0 && y!=0. Ma quando si utilizza la moltiplicazione è necessario fare attenzione agli overflow. Quando si usano numeri interi a 32 bit x = y = 65536 (e diverse altre combinazioni di potenze di due) si ottengono anche x * y = 0 .
Martin Ender,

Sì, è giusto. L'ho usato come controllo di limiti di array bidimensionale qui: codegolf.stackexchange.com/a/37571/31477 dove non importava. Modificherò questi punti.
Baldrickk,

1
Si noti tuttavia che &&ha un comportamento di corto circuito che *manca. Ad esempio, non puoi sostituirlo i++!=0&&j++!=0con i++*j++.
Celtschk,

@celtschk sì, buon punto. Ma se stai puramente facendo l'algebra booleana, allora funziona
Baldrickk, il

11

Usa i seguenti tipi:

u64, s64, u32, s32 (or int)

Per parole / tipi ripetitivi, utilizzare #defines:

#define a while

Ne vale la pena solo se usi whilemolto per compensare i 10 personaggi extra. ( Circa 4. )


1
I tipi u64, s64, u32 e s32 non fanno parte di C ++. Potrebbero essere un'estensione non standard del tuo compilatore (non li ho mai visti, comunque).
Celtschk,

5
Questi due consigli sarebbero meglio collocati in due risposte separate in modo che possano essere votati individualmente.
trichoplax,


10

Quando possibile, cambiare &&e ||per &e |rispettivamente.

Quando si usano istruzioni if ​​semplici:

if(<condition>)<stuff>;

può essere modificato in:

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

che salva un personaggio.


8

Invece di usare while(1), usare for(;;), salvare un carattere :)


8

L'uso dell'operatore virgola al posto delle parentesi graffe aperte e chiuse può salvare alcuni caratteri, se hai una situazione in cui le tue clausole contengono più di un'istruzione:

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

vs.

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

Due personaggi salvati su un semplice IF, o tre in totale per un IF / ELSE.

Come punto di distinzione tra C e C ++, il risultato di un'espressione virgola in C ++ nel suo insieme può essere usato come un valore in valore ... FWIW.


7

Poiché gli elementi dell'array vengono memorizzati uno dopo l'altro in memoria, anziché qualcosa del genere:

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

Puoi fare qualcosa del genere:

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

Ovviamente nessuna delle precedenti è giocata a golf, per leggibilità, ma l'uso esplicito di puntatori può farti risparmiare molto spazio.


Non dimenticare che puoi tagliare tutto quello spazio bianco! (Suggerimento completamente diverso, ma dovrebbe essere menzionato)
stokastic

@stokastic Gli esempi non sono pensati per essere giocati a golf, ma solo per dimostrare come usare la tecnica.
Stuntddude,

6
perchè no for(int* i=array; i<array+25*25; i++)? Quindi devi solo tenere traccia di una variabile.
Lucas,

6

Abbastanza ovvio, ma se stai usando molto della libreria standard, using namespace std;potresti salvare alcuni caratteri.


5
Se usi solo un singolo nome, ma abbastanza spesso, using std::name;potrebbe essere più breve, però.
celtschk

10
Questo salva i caratteri solo se lo usi std::cinque o più volte.
nyuszika7h

6

È utile ricordare che a[i]è lo stesso di *(a+i).

Sostituisci a[0]con *aper un risparmio di due caratteri. Inoltre, a[i][0]equivale a *a[i]e a[0][i]si riduce ai[*a] . Quindi, se stai codificando un 0indice nel tuo array, probabilmente esiste un modo migliore.


5

Invece di scrivere grandi poteri di 10, usa la notazione . Ad esempio, a=1000000000è più lungo di a=1e9. Questo può essere esteso ad altri numeri come a=1e9+24è meglio di a=1000000024.


1
Si noti che questo non è esattamente equivalente, è necessario eseguire il cast in tipi interi prima dell'uso. Ad esempio 1e9/xnon è lo stesso di 1000000000/xo int(1e9)/x.
user202729

5

È possibile utilizzare l'operatore ternario ?:senza alcuna espressione nel blocco reale (salva un byte)

#include <iostream>

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

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

Controlla qui


5
Questa sembra essere un'estensione GNU e non nello standard C ++. https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Conditionals.html#Conditionals
ceilingcat

? R foo (): 0; // if (r) foo () va bene ;;;;; ma per questo r?: foo (); non lo so
RosLuP

5

Intestazione più corta

Questo è specifico di GCC, potrebbe essere estendibile ad altri compilatori.

Intestazione precompilata.

In G ++ bits/stdc++.hè l'intestazione precompilata composta da tutte le altre intestazioni. Se hai bisogno di import2 diversi puoi semplicemente usare questo.

Intestazione più corta.

Queste sono tutte le intestazioni elencate su http://en.cppreference.com/w/cpp/header :

ordinati in ordine crescente di lunghezza.

Alcuni di essi sono già più lunghi di bits/stdc++.he alcuni richiedono il supporto C ++ 17. Alcuni altri non sono supportati da TIO G ++ (per motivi che non conosco). Filtrali li abbiamo:

Può succedere che alcuni di essi possano essere sostituiti da altri più corti. Basta cercare binariamente se quello che ti serve può essere sostituito. In particolare:

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

#importinvece di #includedarti un altro byte.

Inoltre, il carattere spazio tra #importe intestazione non è necessariamente:

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

E se hai bisogno di qualcosa dall'intestazione stdlib, puoi importare qualsiasi intestazione con contenitore STL (preferibile seto map) invece di cstdlib.


3

Operazioni aritmetiche sui booleani:

Sebbene

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

è meglio di

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

non è buono come

a*=(b>0)-.5

Inoltre, usando #define su tutto ciò che viene usato molto. Spesso è più breve dell'uso delle funzioni, poiché i nomi dei tipi non sono necessari.

Combina le cose il più possibile:

a+=a--;

equivale a

a=2*a-1;

Mentre i tuoi esempi sono corretti, fai attenzione a invocare un comportamento indefinito quando lo usi xcome un valore e x++come un valore. comportamento indefinito e punti sequenza
ceilingcat

Sì possibile a + = a--; ha un comportamento indefinito
RosLuP

3

Usa lambda generici come modelli economici

Per tipi diversi da int, usarli come argomenti di funzione può essere costoso. Tuttavia, sono stati introdotti lambda generici (in C ++ 14?) E consentono a qualsiasi lambda di essere un modello - l'utilizzo autoper i tipi di argomento può salvare byte. Confrontare:

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

I lambda generici sono anche molto comodi per accettare gli iteratori - probabilmente il modo migliore per accettare input di array in C ++ è [](auto a, auto z), dove ae zsono passati come begin()e end()dell'array / vector / list / etc.


2

Nel mio primo tentativo di code golf per l' attività "Sottrai i numeri successivi" ho iniziato dalla funzione (58 byte)

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

quindi salva 5 byte con lo spostamento su lambda e spostando l'inizializzazione su for(53)

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

e finalmente dopo il passaggio da fora whileho ottenuto 51 byte:

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

Il codice di prova non salvato è simile a:

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

AGGIORNARE:

In realtà forpuò raggiungere la stessa lunghezza di while:

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

2

Un po 'tardi alla festa, immagino ...

Se vuoi trasformare un'espressione in -1 e 1 invece di 0 e 1, invece di questo:

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

Fai questo:

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

Può salvare alcuni byte a seconda dell'utilizzo.


Invece di int x=(a*10>5)*2-1;, non potresti farlo int x=a*10>5?1:-1;, che è 1 byte più corto?
girobuz,

2

Se si desidera scambiare due variabili intere aeb, quindi

a^=b^=a^=b;

può essere utilizzato, salvando 5 caratteri rispetto al modo standard

a+=b;
b=a-b;
a-=b;

1
A proposito di quel modo standard. ,tagli ints creati in precedenza e quindi t=a;a=b;b=t;sarebbero già stati 3 byte più brevi del a+=b;b=a-b;a-=b;. Tuttavia, il tuo a^=b^=a^=b;è ancora più breve di quello, quindi +1 da me. Non conosco C ++, ma funziona davvero . Come golfista di codice Java sono triste , non sembra funzionare lì . :(
Kevin Cruijssen,

1
@KevinCruijssen Sì, avrei dovuto menzionare C ++, non conosco molto Java, ma a^=b;b^=a;a^=b;funziona bene in Java.
joker007,

1
Non è necessario menzionare esplicitamente il C ++. Tutti questi suggerimenti sono per C ++. :) Come sviluppatore Java ero solo curioso di sapere se qualcosa di simile potesse essere fatto in Java, ma apparentemente no. a^=b;b^=a;a^=b;funziona davvero, ma è più lungo del ,t+ t=a;a=b;b=t;. Mi dispiace menzionare Java, dato che qui è fuori tema. Ma un bel consiglio per i codegolfer C ++!
Kevin Cruijssen,

2

Usa i builtin di GCC invece di importarli

Se stai usando un compilatore GCC, a volte aiuta a usare le loro funzioni integrate, come __builtin_putso __builtin_clz. Per esempio,

44 byte:

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

50 byte:

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

1

Se stai eseguendo C ++ 11 o versioni successive (che dovrebbe essere sempre il caso ora), utilizza autoper tipi complessi, se possibile.

Esempio: 54 byte anziché 66

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

Inoltre, poiché le prestazioni non contano, per alcune sfide si std::listpuò fare il lavoro per pochi byte in meno:

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

1

Le funzioni <algorithm>spesso richiedono il passaggio, a.begin(),a.end()che è molto lungo, invece è possibile utilizzare &a[0],&*end(a)per salvare 3 byte se aè vectoro string.

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

0

Non usare string(""), usa "". Salva 8 byte.


Non è esattamente equivalente. Ad esempio "" + 'a'è char* + char, che è l'aggiunta del puntatore, mentre std::string("") + 'a'è std::string + char- concatenazione di stringhe. string()funzionerebbe.
user202729
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.