Wskazówki dotyczące gry w golfa w C ++

48

Jakie masz ogólne wskazówki na temat gry w golfa w C ++? Szukam pomysłów, które można by zastosować do problemów z golfem w kodzie, które są przynajmniej nieco specyficzne dla C ++ (np. „Usuń komentarze” nie jest odpowiedzią). Proszę zamieścić jedną wskazówkę na odpowiedź.

marcog
źródło
4
Wiele wskazówek dotyczących gry w golfa w C ma również zastosowanie do C ++, więc proszę założyć, że czytelnicy znają to pytanie; pisz tutaj tylko, jeśli masz coś, co nie jest również ważną wskazówką golfową C.
Toby Speight
@TobySpeight Prawdopodobnie dlatego, że oprócz identyfikatora pytania mają ten sam adres URL.
NoOneIsHere
C i C ++, nawet jeśli nie są typem „golfa”, są właściwe i łatwe (jeśli weźmie się pod uwagę odpowiedni podzbiór C ++)
RosLuP

Odpowiedzi:

24

Trójskładnikowy operator warunkowy ?:może być często używany jako zastępstwo dla prostych if- elsewyciągów przy znacznych oszczędnościach.

Ma to szczególną wartość, ponieważ może być używane do wybierania alternatywnych wartości lv, jak w

#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
źródło
nie uruchomiłem jeszcze kodu, ale nie sądzę, że działa tak, jak mówisz. ((u = atoi (v [c]))% 2? o: e) + = u robi nic poza dodaniem wartości u do wyrażenia po lewej stronie, która otrzymuje wartość o lub e, ale zmienne o i e pozostają niezmienione, więc zawsze będą wynosić 0. sprawdź kod, aby zobaczyć, co się stanie. powinieneś użyć adresów, aby to zadziałało
Bogdan Alexandru
4
@BogdanAlexandru Er ... uruchom go. To naprawdę działa. Wartość wyrażenia w nawiasach odnosi się do jednego lub drugiego z ei o. Zauważ, że różni się to od działania tego operatora w c, w którym ta sztuczka nie działa, ponieważ nie może być wartością.
dmckee,
Wymień std::endlsię '\n', że oszczędza 5 znaków
Mukul Kumar
3
@MukulKumar Cóż, tak. Ale dla celów zademonstrowania tej wskazówki zostawiłem wszystko oprócz trójskładnikowej gry w golfa dla przejrzystości.
dmckee
22

Czasami można zapisać dwa znaki, wykorzystując fakt, że statyczne zmienne czasu przechowywania (w szczególności wszystkie globalne zmienne zakresu) są automatycznie inicjalizowane na początku na zero (w przeciwieństwie do zmiennych automatycznych, w których nie ma takiej gwarancji). Więc zamiast

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

Możesz pisać

int a;
int main()
{
  // ...
}
celtschk
źródło
+1, ale zdecydowanie zła praktyka
mondlos
@mondlos: Gra w golfa zasadniczo oznacza złe praktyki.
celtschk
15

Niektóre kompilatory (np. GCC) obsługują stałe wieloznakowe . Dzięki temu można zapisać kilka znaków, gdy wymagana jest duża liczba całkowita. Przykład:

int n='  ';

Wartość zależy od implementacji. Zwykle wartość 'ab'wynosi 256*'a'+'b'lub 'a'+256*'b'. Między znakami cudzysłowu można podać do 4 znaków.

marcog
źródło
3
GCC? Masz na myśli g ++ ?
Nathan Osman
6
@George Edison: GCC oznacza kolekcję kompilatorów GNU , która obejmuje wszystkie jej interfejsy, w tym C, C ++, Go itp.
Joey Adams,
@Joey: Wiem, ale to także nazwa kompilatora GNU C.
Nathan Osman
25
@George: Kompilator GNU C nazywa się gcc, a nie GCC.
fredoverflow
Równie dobrze mogę o tym pamiętać, mógłbym zapomnieć.
12

Jeden, który mi się przydał:

Korzystając z faktu, że wartości niezerowe są truewyrażane w wyrażeniach boolowskich, i to x&&yma znaczenie w x*yprzypadku wartości logicznych

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

ocenia na

(x*y)

Musisz tylko pamiętać o przepełnieniu, jak wskazano poniżej.

Baldrickk
źródło
2
Technicznie jest x!=0 && y!=0. Ale gdy używasz mnożenia, musisz uważać na przepełnienia. Przy użyciu 32-bitowych liczb całkowitych x = y = 65536 (i kilka innych kombinacji potęg dwóch) dałoby również x * y = 0 .
Martin Ender
Tak to prawda. Użyłem go jako dwuwymiarowej tablicy, sprawdź tutaj: codegolf.stackexchange.com/a/37571/31477, gdzie to nie miało znaczenia. Zmienię te punkty w.
Baldrickk,
1
Należy jednak pamiętać, że &&ma zachowanie zwarciowe, którego *brakuje. Na przykład, nie można zastąpić i++!=0&&j++!=0z i++*j++.
celtschk
@celtschk tak, dobra uwaga. Ale jeśli używasz wyłącznie algebry boolowskiej, to działa
Baldrickk
11

Użyj następujących typów:

u64, s64, u32, s32 (or int)

W przypadku powtarzających się słów / typów użyj #defines:

#define a while

Warto, jeśli whiledużo używasz, aby nadrobić dodatkowe 10 znaków. ( Około 4. )

Mateen Ulhaq
źródło
1
Typy u64, s64, u32 i s32 nie są częścią C ++. Mogą być niestandardowym rozszerzeniem twojego kompilatora (chociaż nigdy ich nie widziałem).
celtschk
5
Te dwie wskazówki lepiej byłoby umieścić w dwóch osobnych odpowiedziach, aby można było głosować indywidualnie.
trichoplax
11

Jeśli chcesz używać C ++ 0x, możesz użyć nowych funkcji, takich jak lambdas .

Ślimak mechaniczny
źródło
10

Gdy jest to możliwe, należy zmienić &&i ||na &i |odpowiednio.

Podczas korzystania z prostych instrukcji if:

if(<condition>)<stuff>;

można zmienić na:

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

co ratuje postać.

Alex Gittemeier
źródło
8

Zamiast używać while(1), użyj for(;;), zapisując jedną postać :)

NaCl
źródło
8

Używanie operatora przecinku zamiast otwierania i zamykania nawiasów klamrowych może uratować kilka znaków, jeśli masz sytuację, w której klauzule zawierają więcej niż jedną instrukcję:

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

Dwa znaki zapisane na zwykłym IF lub trzy łącznie dla IF / ELSE.

Jako punkt rozróżnienia między C i C ++ wynik wyrażenia przecinkowego w C ++ jako całości może być użyty jako wartość ... FWIW.

Dr. Rebmu
źródło
7

Ponieważ elementy tablic są przechowywane bezpośrednio w pamięci, zamiast czegoś takiego:

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

Możesz zrobić coś takiego:

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

Oczywiście żadne z powyższych nie jest golfem, dla czytelności, ale wyraźne użycie wskaźników może zaoszczędzić dużo miejsca.

Stuntddude
źródło
Nie zapomnij, że możesz wyciąć wszystkie te białe znaki! (
Zupełnie
@stokastic Przykłady nie są przeznaczone do gry w golfa, a jedynie w celu pokazania, jak korzystać z tej techniki.
Stuntddude
6
dlaczego nie for(int* i=array; i<array+25*25; i++)? Następnie musisz śledzić tylko jedną zmienną.
Lucas,
6

To dość oczywiste, ale jeśli używasz dużo standardowej biblioteki, using namespace std;może zapisać kilka znaków.

developerbmw
źródło
5
Jeśli używasz tylko jednej nazwy, ale dość często, using std::name;może być ona krótsza.
celtschk
10
Zapisuje to tylko postacie, jeśli użyjesz ich std::pięć lub więcej razy.
nyuszika7h
6

Warto pamiętać, że a[i]jest to to samo co *(a+i).

Wymienić a[0]z *adwóch znaków oszczędności. Ponadto a[i][0]jest równoważne *a[i]i a[0][i]zmniejsza się do i[*a]. Jeśli więc kodujesz 0indeks na stałe w swojej tablicy, prawdopodobnie istnieje lepszy sposób.

MegaTom
źródło
5

Zamiast pisać duże potęgi 10, użyj notacji e . Na przykład a=1000000000jest dłuższy niż a=1e9. Można to rozszerzyć na inne liczby, takie jak a=1e9+24lepsze niż a=1000000024.

Pranjal Jain
źródło
1
Pamiętaj, że nie jest to dokładnie równoważne, przed użyciem musisz rzutować na typy całkowite. Na przykład 1e9/xto nie to samo co 1000000000/xlub int(1e9)/x.
user202729,
5

Możesz użyć operatora trójskładnikowego ?:bez żadnych wyrażeń w bloku true (zapisuje on bajt)

#include <iostream>

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

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

Sprawdź tutaj

x1Mike7x
źródło
5
Wydaje się, że jest to rozszerzenie GNU, a nie standard C ++. https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Conditionals.html#Conditionals
ceilingcat
r? foo (): 0; // if (r) foo () to jest ok ;;;;; ale dla tego r?: foo (); nie wiem tego
RosLuP
5

Krótszy nagłówek

Jest to specyficzne dla GCC, może być rozszerzalne na inne kompilatory.

Wstępnie skompilowany nagłówek.

W G ++ bits/stdc++.hnagłówek prekompilowany składa się ze wszystkich innych nagłówków. Jeśli potrzebujesz import2 różnych, możesz po prostu tego użyć.

Krótszy nagłówek.

To są wszystkie nagłówki wymienione na stronie http://en.cppreference.com/w/cpp/header :

posortowane według rosnącej długości.

Niektóre z nich są już dłuższe bits/stdc++.h, a niektóre wymagają obsługi C ++ 17. Niektóre inne nie są obsługiwane przez TIO G ++ (z powodów, których nie znam). Filtruj je:

Może się zdarzyć, że niektóre z nich można zastąpić krótszymi. Wystarczy wyszukać binarnie, czy tego, którego potrzebujesz, można wymienić. W szczególności:

cstdio -> ios        (-3 bytes)
algorithm -> regex   (-4 bytes)
vector -> queue      (-1 byte)
string -> map        (-3 bytes)
bitset -> regex      (-1 byte)
numeric -> random    (-1 byte)
użytkownik202729
źródło
4

#importzamiast #includedaje jeszcze jeden bajt.

Ponadto spacja między #importi nagłówkiem niekoniecznie:

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

A jeśli potrzebujesz czegoś z stdlibnagłówka, możesz zaimportować dowolny nagłówek z kontenerem STL (najlepiej setlub map) zamiast cstdlib.

x1Mike7x
źródło
3

Operacje arytmetyczne na boolach:

Mimo że

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

jest lepszy niż

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

to nie jest tak dobre jak

a*=(b>0)-.5

Używanie #define do wszystkiego, co jest często używane. Często jest krótszy niż używanie funkcji, ponieważ nazwy typów nie są konieczne.

Łącz rzeczy w jak największym stopniu:

a+=a--;

jest taki sam jak

a=2*a-1;
Lucas
źródło
Podczas gdy twoje przykłady są poprawne, bądź ostrożny, powołując się na niezdefiniowane zachowanie, gdy używasz go xjako wartości i x++jako wartości. niezdefiniowane zachowanie i punkty sekwencji
pułapkap
Tak możliwe a + = a--; ma niezdefiniowane zachowanie
RosLuP
3

Używaj ogólnych lambdas jako tanich szablonów

W przypadku typów innych niż intużywanie ich jako argumentów funkcji może być kosztowne. Jednak wprowadzono ogólne lambdy (w C ++ 14?) I pozwalają, aby dowolna lambda była szablonem - użycie autotypów argumentów może zaoszczędzić bajty. Porównać:

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

Ogólne lambdy są również bardzo wygodne do akceptowania iteratorów - prawdopodobnie najlepszym sposobem na akceptację danych wejściowych z tablicy w C ++ jest [](auto a, auto z), gdzie ai zsą przekazywane jako begin()i end()z tablicy / vector / list / etc.

Toby Speight
źródło
2

W mojej pierwszej próbie kodu golfa dla zadania „Odejmij kolejne liczby” Zacząłem od funkcji (58 bajtów)

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

następnie bezpieczne 5 bajtów z przejściem na lambda i przeniesieniem inicjalizacji z for(53)

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

i wreszcie po przejściu z forna whilemam 51 bajtów:

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

Nie testowany kod testowy przypomina:

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

AKTUALIZACJA:

W rzeczywistości formoże osiągnąć taką samą długość jak while:

[](int N,int P){int F=N;for(;P--;F-=++N);return F;}
VolAnd
źródło
2

Trochę późno na imprezę, jak sądzę ...

Jeśli chcesz zmienić wyrażenie na -1 i 1 zamiast 0 i 1, zamiast tego:

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

Zrób to:

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

Może zaoszczędzić niektóre bajty w zależności od użycia.

Yuval Meshorer
źródło
Zamiast tego int x=(a*10>5)*2-1;, nie możesz tego zrobić int x=a*10>5?1:-1;, który jest o 1 bajt krótszy?
girobuz
2

Jeśli chcesz zamienić dwie zmienne całkowite aib, to:

a^=b^=a^=b;

można użyć, oszczędzając 5 znaków niż standardowy sposób

a+=b;
b=a-b;
a-=b;
joker007
źródło
1
O tym standardowym sposobie. ,tw ints utworzonych wcześniej, a wtedy t=a;a=b;b=t;byłyby już o 3 bajty krótsze niż a+=b;b=a-b;a-=b;. Twój a^=b^=a^=b;jest jeszcze krótszy, więc daj +1 ode mnie. Nie znam C ++, ale rzeczywiście działa . Jako golfista Java jestem smutny, że nie działa . :(
Kevin Cruijssen
1
@KevinCruijssen Tak, powinienem był wspomnieć o C ++, nie znam dużo Java, ale a^=b;b^=a;a^=b;działa dobrze w Javie.
joker007
1
Nie trzeba wyraźnie wymieniać C ++. Wszystkie te wskazówki dotyczą C ++. :) Jako programista Java byłem ciekawy, czy coś podobnego można zrobić w Javie, ale najwyraźniej nie. a^=b;b^=a;a^=b;rzeczywiście działa, ale jest dłuższy niż ,t+ t=a;a=b;b=t;. Przepraszam, że wspomniałem o Javie, ponieważ jest tutaj nie na temat. Ale fajna wskazówka dla kodegolfów C ++!
Kevin Cruijssen
2

Używaj wbudowanych GCC zamiast importować

Jeśli używasz kompilatora GCC, czasem pomaga korzystanie z ich wbudowanych funkcji, takich jak __builtin_putslub __builtin_clz. Na przykład,

44 bajty:

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

50 bajtów:

#import<cstdio>
int main(){puts("Hello, world!");}
dingledooper
źródło
1

Jeśli korzystasz z C ++ 11 lub nowszej wersji (co powinno zawsze mieć miejsce teraz), użyj auto, jeśli to możliwe , do typów złożonych.

Przykład: 54 bajty zamiast 66

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

Ponadto, ponieważ wydajność nie ma znaczenia, w przypadku niektórych wyzwań std::listmoże po prostu wykonać zadanie o kilka bajtów mniej:

#include<list>
auto f(std::list<int> l){return l;}
movatica
źródło
1

Funkcje <algorithm>często wymagają przekazania, a.begin(),a.end()które jest naprawdę długie, zamiast tego można użyć &a[0],&*end(a)do zapisania 3 bajtów, jeśli ajest vectorlub string.

sort(a.begin(),a.end());
sort(begin(a),end(a));
sort(&a[0],&*end(a));
JayXon
źródło
0

Nie używaj string(""), używaj "". Oszczędza 8 bajtów.

Rɪᴋᴇʀ
źródło
To nie jest dokładnie równoważne. Na przykład "" + 'a'is char* + char, który jest dodawaniem wskaźnika, natomiast std::string("") + 'a'is std::string + char- konkatenacja ciągów znaków. string()pracowałbym.
user202729,