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.


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

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

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


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.


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


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


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.


8

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


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.


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.


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.


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.


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.


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


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)

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.


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;

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.


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

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.


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;

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

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

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

0

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


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.