Consejos para jugar golf en C ++

48

¿Qué consejos generales tienes para jugar al golf en C ++? Estoy buscando ideas que puedan aplicarse a los problemas de golf de código en general que sean al menos algo específicos para C ++ (por ejemplo, "eliminar comentarios" no es una respuesta). Por favor, publique un consejo por respuesta.

marcog
fuente
44
Muchos de los consejos para jugar golf en C también son aplicables a C ++, por lo tanto, suponga que los lectores están familiarizados con esa pregunta; solo publique aquí si tiene algo que no es también un consejo válido de golf C.
Toby Speight
@TobySpeight Probablemente porque tienen la misma URL además del ID de la pregunta.
NoOneIsHere
C y C ++, incluso si no son del tipo "golf", son correctos y fáciles (si se considera el subconjunto correcto de C ++)
RosLuP

Respuestas:

24

El operador condicional ternario ?:menudo se puede utilizar como soporte en por sencilla if- elsedeclaraciones en un ahorro considerable.

Tiene un valor especial porque puede usarse para seleccionar valores alternativos como en

#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
fuente
no he ejecutado el código todavía, pero no creo que funcione de la manera que dices. ((u = atoi (v [c]))% 2? o: e) + = u no hace más que agregar el valor u a la expresión de la izquierda que obtiene el valor o o e, pero las variables o y e permanecen sin cambios, por lo que siempre serán 0. verifique el código para ver qué sucede. deberías usar direcciones para que funcione
Bogdan Alexandru
44
@BogdanAlexandru Er ... ejecútalo. Realmente funciona. El valor de la expresión entre paréntesis es una referencia a uno u otro de ey o. Tenga en cuenta que esto es diferente de cómo funciona este operador en c donde este truco no funciona porque no puede ser un valor.
dmckee
Reemplazar std::endlcon '\n'eso ahorra 5 caracteres
Mukul Kumar
3
@MukulKumar Bueno, sí. Pero con el fin de demostrar este consejo, dejé todo, excepto el ternario condicional sin golf para mayor claridad.
dmckee
22

A veces puede guardar dos caracteres utilizando el hecho de que las variables de duración de almacenamiento estático (que incluye especialmente todas las variables de alcance global) se inicializan automáticamente al cero al principio (a diferencia de las variables automáticas donde no tiene dicha garantía). Entonces en lugar de

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

puedes escribir

int a;
int main()
{
  // ...
}
celtschk
fuente
+1 pero definitivamente es una mala práctica
lunes
@mondlos: Jugar golf básicamente implica una mala práctica.
celtschk
15

Algunos compiladores (por ejemplo, GCC) admiten constantes de varios caracteres . Esto puede guardar algunos caracteres cuando se requiere un valor entero grande. Ejemplo:

int n='  ';

El valor es específico de la implementación. Por lo general, el valor de 'ab'es 256*'a'+'b'o 'a'+256*'b'. Puede especificar hasta 4 caracteres entre comillas.

marcog
fuente
3
GCC? ¿Te refieres a g ++ ?
Nathan Osman
66
@George Edison: GCC representa la Colección del Compilador GNU , que abarca todas sus interfaces, incluidas las de C, C ++, Go, etc.
Joey Adams
@Joey: Lo sé, pero también es el nombre del compilador C de GNU.
Nathan Osman
25
@George: El compilador GNU C se llama gcc, no GCC.
fredoverflow
Bien podría recordar eso, podría olvidarlo.
12

Uno que me pareció útil:

Aprovechando el hecho de que los valores distintos de cero se evalúan trueen expresiones booleanas, y eso se x&&yevalúa x*ycuando se trata de valores booleanos

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

evalúa a

(x*y)

Solo debe tener en cuenta los desbordamientos, como se señala a continuación.

Baldrickk
fuente
2
Técnicamente, lo es x!=0 && y!=0. Pero cuando se usa la multiplicación, debes tener cuidado con los desbordamientos. Cuando se usan enteros de 32 bits, x = y = 65536 (y varias otras combinaciones de potencias de dos) también producirían x * y = 0 .
Martin Ender
Si, eso es correcto. Lo usé como una matriz de límites de matriz bidimensional aquí: codegolf.stackexchange.com/a/37571/31477 donde eso no importaba. Editaré esos puntos.
Baldrickk
1
Sin embargo, &&tenga en cuenta que tiene un comportamiento de cortocircuito que le *falta. Por ejemplo, no puede reemplazar i++!=0&&j++!=0con i++*j++.
celtschk
@celtschk sí, buen punto. Pero si solo estás haciendo el álgebra booleana, entonces funciona
Baldrickk
11

Use los siguientes tipos:

u64, s64, u32, s32 (or int)

Para palabras / tipos repetitivos, use #defines:

#define a while

Solo vale la pena si usa whilemucho para compensar los 10 caracteres adicionales. ( Alrededor de 4. )

Mateen Ulhaq
fuente
1
Los tipos u64, s64, u32 y s32 no son parte de C ++. Pueden ser una extensión no estándar de su compilador (aunque nunca los he visto).
celtschk
55
Estos dos consejos se ubicarían mejor en dos respuestas separadas para que se puedan votar individualmente.
trichoplax
11

Si está dispuesto a usar C ++ 0x, puede usar nuevas funciones como lambdas .

Caracol mecánico
fuente
10

Cuando sea posible, cambie &&y ||a &y |respectivamente.

Cuando se usan declaraciones if simples:

if(<condition>)<stuff>;

se puede cambiar a:

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

que salva a un personaje

Alex Gittemeier
fuente
8

En lugar de usar while(1), usar for(;;), guardar un personaje :)

NaCl
fuente
8

El uso del operador de coma en lugar de llaves abiertas y cerradas puede guardar algunos caracteres, si tiene una situación en la que sus cláusulas tienen más de una declaración:

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

Dos caracteres guardados en un IF simple, o tres en total para un IF / ELSE.

Como un punto de distinción entre C y C ++, el resultado de una expresión de coma en C ++ en su conjunto puede usarse como un valor l ... FWIW.

Dr. Rebmu
fuente
7

Dado que los elementos de la matriz se almacenan directamente uno tras otro en la memoria, en lugar de algo como esto:

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

Puedes hacer algo como esto:

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

Obviamente, ninguno de los anteriores es golf, para facilitar la lectura, pero el uso explícito de punteros puede ahorrarle mucho espacio.

Stuntddude
fuente
¡No olvides que puedes cortar todo ese espacio en blanco! (
Consejo
@stokastic Los ejemplos no son para jugar al golf, solo para demostrar cómo usar la técnica.
Stuntddude
66
¿por qué no for(int* i=array; i<array+25*25; i++)? Entonces solo tiene que hacer un seguimiento de una variable.
Lucas
6

Es bastante obvio, pero si está utilizando una gran cantidad de la biblioteca estándar, using namespace std;puede guardar algunos caracteres.

desarrolladorbmw
fuente
55
Sin embargo, si solo usa un solo nombre, pero con bastante frecuencia, using std::name;puede ser más corto.
celtschk
10
Esto solo guarda caracteres si los usa std::cinco o más veces.
nyuszika7h
6

Es útil recordar que a[i]es lo mismo que *(a+i).

Reemplazar a[0]con *ados ahorro de caracteres. Además, a[i][0]es equivalente *a[i]y se a[0][i]reduce a i[*a]. Entonces, si está codificando un 0índice en su matriz, probablemente exista una mejor manera.

MegaTom
fuente
5

En lugar de escribir grandes potencias de 10, usa la notación e . Por ejemplo, a=1000000000es más largo que a=1e9. Esto se puede extender a otros números como a=1e9+24es mejor que a=1000000024.

Pranjal Jain
fuente
1
Tenga en cuenta que esto no es exactamente equivalente, necesita convertir a tipos enteros antes de usar. Por ejemplo, 1e9/xno es lo mismo que 1000000000/xo int(1e9)/x.
user202729
5

Puede usar el operador ternario ?:sin ninguna expresión en el bloque verdadero (guarda un byte)

#include <iostream>

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

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

Compruébalo aquí

x1Mike7x
fuente
55
Esto parece ser una extensión de GNU y no en el estándar C ++. https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Conditionals.html#Conditionals
ceilingcat el
r? foo (): 0; // if (r) foo () esto está bien ;;;;; pero para esto r?: foo (); no lo sé
RosLuP
5

Encabezado más corto

Esto es específico de GCC, puede ser extensible a otros compiladores.

Encabezado precompilado.

En G ++, bits/stdc++.hel encabezado precompilado consiste en todos los demás encabezados. Si necesita import2 diferentes, simplemente puede usar esto.

Encabezado más corto.

Estos son todos los encabezados enumerados en http://en.cppreference.com/w/cpp/header :

ordenados en orden creciente de longitud.

Algunos de ellos ya son más largos bits/stdc++.hy otros requieren compatibilidad con C ++ 17. Algunos otros no son compatibles con TIO G ++ (por razones que no conozco). Filtrarlos tenemos:

Puede suceder que algunos de ellos puedan ser reemplazados por otros más cortos. Solo busque binariamente si puede reemplazar la que necesita. En particular:

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

#importen lugar de #includedarte un byte más.

Además, el carácter de espacio entre #importy el encabezado no es necesariamente:

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

Y si necesita algo del stdlibencabezado, puede importar cualquier encabezado con el contenedor STL (preferible seto map) en lugar de cstdlib.

x1Mike7x
fuente
3

Operaciones aritméticas en booleanos:

A pesar de que

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

es mejor que

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

no es tan bueno como

a*=(b>0)-.5

Además, usar #define en cualquier cosa que se use mucho. A menudo es más corto que usar funciones, ya que los nombres de tipo no son necesarios.

Combina las cosas tanto como sea posible:

a+=a--;

es lo mismo que

a=2*a-1;
Lucas
fuente
Si bien sus ejemplos son correctos, tenga cuidado de invocar un comportamiento indefinido cuando lo use xcomo un valor y x++como un valor. comportamiento indefinido y puntos de secuencia
ceilingcat
Sí posible a + = a--; tiene comportamiento indefinido
RosLuP
3

Usa lambdas genéricas como plantillas baratas

Para otros tipos que no sean int, usarlos como argumentos de función puede ser costoso. Sin embargo, se introdujeron lambdas genéricas (en C ++ 14?) Y permiten que cualquier lambda sea una plantilla; el uso autode los tipos de argumento puede guardar bytes. Comparar:

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

Las lambdas genéricas también son muy convenientes para aceptar iteradores: probablemente la mejor manera de aceptar entradas de matriz en C ++ es [](auto a, auto z), dónde ay zse pasan como begin()y end()de la matriz / vector / lista / etc.

Toby Speight
fuente
2

En mi primer intento de codificar golf para la tarea "Restar los siguientes números" , comencé desde la función (58 bytes)

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

luego a salvo 5 bytes con cambiar a lambda y mover la inicialización de for(53)

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

y finalmente después de cambiar de fora whileobtuve 51 bytes:

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

El código de prueba sin golf es algo así como:

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

ACTUALIZAR:

En realidad forpuede alcanzar la misma longitud que while:

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

Un poco tarde para la fiesta, supongo ...

Si desea convertir una expresión en -1 y 1 en lugar de 0 y 1, en lugar de esto:

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

hacer esto:

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

Puede guardar algunos bytes dependiendo del uso.

Yuval Meshorer
fuente
En lugar de int x=(a*10>5)*2-1;, ¿no podrías hacerlo int x=a*10>5?1:-1;, que es 1 byte más corto?
girobuz
2

Si desea intercambiar dos variables enteras a y b, entonces,

a^=b^=a^=b;

se puede usar, ahorrando 5 caracteres que la forma estándar

a+=b;
b=a-b;
a-=b;
joker007
fuente
1
Sobre esa forma estándar. ,ten los ints creados anteriormente y luego t=a;a=b;b=t;ya habrían sido 3 bytes más cortos que el a+=b;b=a-b;a-=b;. Aún así, tu a^=b^=a^=b;es aún más corto que eso, entonces +1 de mi parte. No sé C ++, pero de hecho funciona . Como jugador de código Java, estoy triste porque no parece funcionar allí . :(
Kevin Cruijssen
1
@KevinCruijssen Sí, debería haber mencionado C ++, no sé mucho sobre Java, pero a^=b;b^=a;a^=b;funciona bien en Java.
joker007
1
No es necesario mencionar explícitamente C ++. Todos estos consejos son para C ++. :) Como desarrollador de Java, tenía curiosidad por saber si se podría hacer algo similar en Java, pero aparentemente no. a^=b;b^=a;a^=b;de hecho funciona, pero es más largo que el ,t+ t=a;a=b;b=t;. Perdón por mencionar Java, ya que está fuera de tema aquí. ¡Pero un buen consejo para los codegolfers de C ++!
Kevin Cruijssen
2

Utilice los componentes incorporados de GCC en lugar de importar

Si está utilizando un compilador GCC, a veces ayuda usar sus funciones integradas, como __builtin_putso __builtin_clz. Por ejemplo,

44 bytes:

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

50 bytes:

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

Si está haciendo C ++ 11 o más reciente (que siempre debería ser el caso ahora), utilícelo autopara tipos complejos, si es posible.

Ejemplo: 54 bytes en lugar de 66

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

Además, como el rendimiento no importa, para algunos desafíos, a std::listsolo puede hacer el trabajo por unos pocos bytes menos:

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

Las funciones a <algorithm>menudo requieren pasar, lo a.begin(),a.end()cual es realmente largo, en su lugar puede usar &a[0],&*end(a)para guardar 3 bytes si aes vectoro string.

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

No uses string(""), usa "". Ahorra 8 bytes.

Rɪᴋᴇʀ
fuente
No es exactamente equivalente. Por ejemplo , "" + 'a'es char* + char, que es la suma del puntero, mientras que std::string("") + 'a'es std::string + char- concatenación de cadenas. string()trabajaría.
user202729