Astuces pour jouer au golf en C ++

48

Quels conseils généraux avez-vous pour jouer au golf en C ++? Je recherche des idées pouvant être appliquées aux problèmes de code de golf en général, qui sont au moins quelque peu spécifiques au C ++ (par exemple, "supprimer les commentaires" n'est pas une réponse). Merci de poster un pourboire par réponse.

marcog
la source
4
La plupart des astuces pour jouer au golf en C s’appliquent également à C ++. Supposons donc que les lecteurs connaissent bien cette question. ne postez ici que si vous avez quelque chose qui n’est pas aussi un bon conseil de golf.
Toby Speight le
@TobySpeight Probablement parce qu'ils ont la même URL que l'ID de la question.
NoOneIsHere
C et C ++, même s'ils ne sont pas du type 'golfing', sont
corrects

Réponses:

24

L'opérateur conditionnel ternaire ?:peut souvent être utilisé comme support dans des simples if- elsedéclarations à des économies considérables.

Il a une valeur particulière en ce qu’il peut être utilisé pour sélectionner des valeurs alternatives comme dans

#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
la source
Je n'ai pas encore exécuté le code, mais je ne pense pas que cela fonctionne comme vous le dites. ((u = atoi (v [c]))% 2? o: e) + = u ne fait que d'ajouter la valeur u à l'expression de gauche qui obtient la valeur o ou e, mais les variables o et e restera inchangé, ils seront donc toujours 0. vérifiez le code pour voir ce qui se passe. vous devriez utiliser les adresses pour que cela fonctionne
Bogdan Alexandru
4
@BogdanAlexandru Er ... faites-le. Ça marche vraiment. La valeur de l'expression entre parenthèses est une référence à l'un ou l'autre de eet o. Notez que ceci diffère de la façon dont cet opérateur fonctionne dans c où cette astuce ne fonctionne pas car elle ne peut pas être une valeur.
dmckee
Remplacer std::endlpar '\n'qui enregistre 5 caractères
Mukul Kumar
3
@MukulKumar Eh bien, oui. Mais dans le but de démontrer ce conseil, j’ai tout laissé, sauf le ternaire-conditionnel qui n’était pas golfé pour plus de clarté.
dmckee
22

Parfois, vous pouvez enregistrer deux caractères en utilisant le fait que les variables de durée de stockage statique (qui incluent notamment toutes les variables de portée globales) sont automatiquement initialisées à zéro au début (contrairement aux variables automatiques pour lesquelles vous n'avez aucune garantie de ce type). Donc au lieu de

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

tu peux écrire

int a;
int main()
{
  // ...
}
celtschk
la source
+1 mais certainement mauvais entraînement
mondlos
@mondlos: Le golf implique fondamentalement une mauvaise pratique.
celtschk
15

Certains compilateurs (par exemple, GCC) prennent en charge les constantes à plusieurs caractères . Cela peut économiser quelques caractères lorsqu'une valeur entière importante est requise. Exemple:

int n='  ';

La valeur est spécifique à l'implémentation. Habituellement, la valeur de 'ab'est 256*'a'+'b'ou 'a'+256*'b'. Vous pouvez spécifier jusqu'à 4 caractères entre les guillemets.

marcog
la source
3
GCC? Vous voulez dire g ++ ?
Nathan Osman
6
@ George Edison: GCC représente la collection de compilateurs GNU , qui englobe toutes ses interfaces, y compris celles de C, C ++, Go, etc.
Joey Adams, le
@ Joey: Je sais, mais c'est aussi le nom du compilateur GNU C.
Nathan Osman
25
@ George: Le compilateur GNU C s'appelle gcc, pas GCC.
fredoverflow
Autant que je m'en souvienne, je pourrais oublier.
12

Celui que j'ai trouvé pratique:

Profitant du fait que les valeurs non nulles sont évaluées à truedes expressions booléennes, et qui x&&yévalue à x*yquand traiter booléens

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

évalue à

(x*y)

Vous devez juste être conscient des débordements, comme indiqué ci-dessous.

Baldrickk
la source
2
Techniquement, c'est x!=0 && y!=0. Mais lorsque vous utilisez la multiplication, vous devez faire attention aux débordements. Lors de l'utilisation d'entiers 32 bits, x = y = 65 536 (et plusieurs autres combinaisons de puissances de deux) donnerait également x * y = 0 .
Martin Ender
Oui c'est vrai. Je l'ai utilisé comme un tableau bidimensionnel aux limites de vérifier ici: codegolf.stackexchange.com/a/37571/31477 où cela n'a pas d'importance. Je modifierai ces points.
Baldrickk le
1
Notez cependant que cela &&a un comportement de court-circuit qui *manque. Par exemple, vous ne pouvez pas remplacer i++!=0&&j++!=0par i++*j++.
celtschk
@celtschk oui, bon point. Mais si vous ne faites que l’algèbre booléenne, alors cela fonctionne
Baldrickk
11

Utilisez les types suivants:

u64, s64, u32, s32 (or int)

Pour les mots / types répétitifs, utilisez #defines:

#define a while

Cela ne vaut que si vous utilisez whilebeaucoup pour compenser les 10 caractères supplémentaires. ( Environ 4. )

Mateen Ulhaq
la source
1
Les types u64, s64, u32 et s32 ne font pas partie de C ++. Il peut s’agir d’une extension non standard de votre compilateur (je ne les ai jamais vues, cependant).
celtschk
5
Ces deux astuces seraient mieux placées dans deux réponses séparées pour pouvoir être votées individuellement.
Trichoplax
11

Si vous souhaitez utiliser C ++ 0x, vous pouvez utiliser de nouvelles fonctionnalités telles que lambdas .

Escargot mécanique
la source
10

Si possible, changez &&et ||à &et |respectivement.

Lorsque vous utilisez des instructions if simples:

if(<condition>)<stuff>;

peut être changé en:

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

ce qui sauve un personnage.

Alex Gittemeier
la source
8

Au lieu d'utiliser while(1), utilisez for(;;), en sauvegardant un caractère :)

NaCl
la source
8

L'utilisation de l'opérateur virgule au lieu d'accolades ouvrantes et fermantes peut enregistrer quelques caractères, si vous avez une situation dans laquelle vos clauses contiennent plusieurs instructions:

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

contre.

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

Deux caractères enregistrés sur un IF simple ou trois au total pour un IF / ELSE.

En tant que point de distinction entre C et C ++, le résultat d'une expression de virgule en C ++ dans son ensemble peut être utilisé sous la forme d'une valeur lvalue ... FWIW.

Dr. Rebmu
la source
7

Puisque les éléments de tableau sont stockés directement les uns après les autres en mémoire, au lieu de quelque chose comme ceci:

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

Vous pouvez faire quelque chose comme ça:

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

Évidemment, ni l’un ni l’autre des éléments ci-dessus n’est golfé, par souci de lisibilité, mais l’utilisation explicite de pointeurs peut vous faire économiser beaucoup d’espace.

Stuntddude
la source
N'oubliez pas que vous pouvez supprimer tous les espaces! (
Astuce
@stokastic Les exemples ne sont pas destinés à être joués au golf, mais simplement à montrer comment utiliser cette technique.
Stuntddude
6
pourquoi pas for(int* i=array; i<array+25*25; i++)? Ensuite, vous ne devez suivre qu'une seule variable.
Lucas
6

C’est une évidence, mais si vous utilisez beaucoup de la bibliothèque standard, vous using namespace std;pourriez économiser quelques caractères.

developerbmw
la source
5
Si vous utilisez un seul nom, mais cela using std::name;peut souvent être plus court.
celtschk
10
Cela enregistre uniquement les caractères si vous utilisez std::cinq fois ou plus.
nyuszika7h
6

Il est utile de se rappeler que a[i]c'est la même chose que *(a+i).

Remplacez a[0]par *apour deux économies de caractères. En outre, a[i][0]est équivalent à *a[i]et a[0][i]réduit à i[*a]. Donc, si vous codez en dur un 0index dans votre tableau, il existe probablement une meilleure solution.

MegaTom
la source
5

Au lieu d'écrire de grandes puissances de 10, utilisez la notation e . Par exemple, a=1000000000est plus long que a=1e9. Cela peut être étendu à d'autres numéros, comme a=1e9+24c'est mieux que a=1000000024.

Pranjal Jain
la source
1
Notez que ce n’est pas exactement équivalent, vous devez convertir les types entiers avant de les utiliser. Par exemple, ce 1e9/xn'est pas la même chose que 1000000000/xou int(1e9)/x.
user202729
5

Vous pouvez utiliser l'opérateur ternaire ?:sans aucune expression dans le bloc true (il enregistre un octet)

#include <iostream>

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

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

Vérifiez ici

x1Mike7x
la source
5
Cela semble être une extension GNU et non dans la norme C ++. https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Conditionals.html#Conditionals
ceilingcat
r? foo (): 0; // if (r) foo () c'est ok ;;;;; mais pour cela r?: foo (); Je ne sais pas que
RosLuP
5

Tête plus courte

Ceci est spécifique à GCC, il peut être extensible à d’autres compilateurs.

En-tête précompilé.

En G ++, bits/stdc++.hl'en-tête précompilé est constitué de tous les autres en-têtes. Si vous avez besoin de import2 types différents, vous pouvez simplement l'utiliser.

En-tête plus court.

Ce sont tous les en-têtes répertoriés sur http://fr.cppreference.com/w/cpp/header :

triés par ordre croissant de longueur.

Certains d'entre eux sont déjà plus longs que bits/stdc++.h, et certains nécessitent le support de C ++ 17. Certains autres ne sont pas pris en charge par TIO G ++ (pour des raisons que je ne connais pas). Filtrez-les nous avons:

Il se peut que certaines d’entre elles puissent être remplacées par des plus courtes. Juste recherche binaire si celle dont vous avez besoin peut être remplacée. En particulier:

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

#importau lieu de #includevous donne un octet supplémentaire.

De plus, le caractère espace entre #importet en-tête n'est pas nécessairement:

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

Et si vous avez besoin de quelque chose à partir de stdlibheader, vous pouvez importer n'importe quel en-tête avec un conteneur STL (préférable setou map) au lieu de cstdlib.

x1Mike7x
la source
3

Opérations arithmétiques sur les booléens:

Bien que

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

est mieux que

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

ce n'est pas aussi bon que

a*=(b>0)-.5

En outre, en utilisant #define sur tout ce qui est beaucoup utilisé. Il est souvent plus court que d'utiliser des fonctions, car les noms de type ne sont pas nécessaires.

Combinez les choses autant que possible:

a+=a--;

est le même que

a=2*a-1;
Lucas
la source
Bien que vos exemples soient corrects, veillez à ne pas invoquer un comportement non défini lors de l'utilisation xde lvalue et x++de rvalue. comportement indéfini et points de séquence
ceilingcat
Oui possible a + = a--; a un comportement non
défini
3

Utilisez des lambdas génériques comme modèles bon marché

Pour les types autres que int, les utiliser comme arguments de fonction peut être coûteux. Cependant, des lambdas génériques ont été introduits (en C ++ 14?) Et permettent à tout lambda d'être un modèle - utiliser autodes types d'argument pour économiser des octets. Comparer:

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

Les lambdas génériques sont également très pratiques pour accepter des itérateurs. Le meilleur moyen d’accepter les entrées de tableau en C ++ est probablement le suivant [](auto a, auto z): où aet zsont passés comme begin()et end()du tableau / vecteur / liste / etc.

Toby Speight
la source
2

Dans ma première tentative de code de golf pour la tâche "Soustraire les nombres suivants" je suis parti de la fonction (58 octets)

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

puis sécurisez 5 octets avec passage à lambda et initialisation déplacée hors de for(53)

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

et finalement, après avoir basculé de forà, whilej'ai obtenu 51 octets:

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

Le code de test ungolfed est quelque chose comme:

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

MISE À JOUR:

En fait , forpeut atteindre la même longueur que while:

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

Un peu tard pour la fête je suppose ...

Si vous voulez transformer une expression en -1 et 1 au lieu de 0 et 1, au lieu de ceci:

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

faire ceci:

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

Cela peut économiser des octets en fonction de l'utilisation.

Yuval Meshorer
la source
Au lieu de int x=(a*10>5)*2-1;, ne pourriez-vous pas faire int x=a*10>5?1:-1;, ce qui est plus court d'un octet?
Girobuz
2

Si vous voulez échanger deux variables entières a et b alors,

a^=b^=a^=b;

peut être utilisé en économisant 5 caractères par rapport à la méthode standard

a+=b;
b=a-b;
a-=b;
joker007
la source
1
De cette manière standard. ,tau début créé et t=a;a=b;b=t;aurait déjà été 3 octets plus court que le a+=b;b=a-b;a-=b;. Pourtant, votre a^=b^=a^=b;est encore plus court que cela, donc +1 de moi. Je ne connais pas le C ++, mais ça fonctionne . En tant que golfeur de code Java, je suis triste que cela ne semble pas fonctionner là-bas . :(
Kevin Cruijssen le
1
@KevinCruijssen Ouais, j'aurais dû parler de C ++, je ne connais pas beaucoup Java, mais a^=b;b^=a;a^=b;fonctionne très bien en Java.
joker007
1
Inutile de mentionner explicitement C ++. Tous ces conseils sont pour C ++. :) En tant que développeur Java, j'étais simplement curieux de savoir si quelque chose de similaire pouvait être fait en Java, mais apparemment pas. a^=b;b^=a;a^=b;fonctionne bien, mais est plus long que le ,t+ t=a;a=b;b=t;. Désolé de mentionner Java, car il est hors sujet ici. Mais bon conseil pour les codegolfeurs C ++!
Kevin Cruijssen
2

Utiliser les fonctions intégrées de GCC au lieu d'importer

Si vous utilisez un compilateur GCC, il est parfois utile d’utiliser leurs fonctions internes, telles que __builtin_putsou __builtin_clz. Par exemple,

44 octets:

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

50 octets:

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

Si vous utilisez C ++ 11 ou plus récent (ce qui devrait toujours être le cas maintenant), utilisez-le autopour les types complexes, si possible.

Exemple: 54 octets au lieu de 66

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

En outre, comme les performances importent peu, il est possible que, pour certains problèmes, std::listil ne vous reste plus qu'à faire le travail pour quelques octets en moins:

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

Les fonctions dans <algorithm>souvent requièrent des passes a.begin(),a.end()vraiment longues, mais vous pouvez utiliser &a[0],&*end(a)pour économiser 3 octets si aest vectorou string.

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

Ne pas utiliser string(""), utilisez "". Il sauve 8 octets.

Rɪᴋᴇʀ
la source
Ce n'est pas exactement équivalent. Par exemple "" + 'a'est char* + char, ce qui est plus de pointeur, tout en std::string("") + 'a'est std::string + char- concaténation de chaînes. string()travaillerait.
user202729