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


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

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

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


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.


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


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


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


8

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


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.


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.


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.


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.


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.


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


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)

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.


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;

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.


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

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.


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;

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

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

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

0

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


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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.