23 unikalne postacie za pomocą Digraphs. (25 bez). Bez UB.
Użyj składni inicjalizatora wzmocnionego C ++ 11, aby zainicjować listę liczb całkowitych do zera, int var{};unikając =i 0. (Lub w twoim przypadku, unikając globalnego iiii). Daje to źródło zer innych niż zmienne globalne (które są statycznie inicjowane na zero, w przeciwieństwie do miejscowych).
Obecne kompilatory domyślnie akceptują tę składnię, bez konieczności włączania żadnych specjalnych opcji.
(Sztuczka z zawijaniem liczb całkowitych jest fajna i dobra do gry w golfa z wyłączoną optymalizacją, ale podpisane przepełnienie jest niezdefiniowanym zachowaniem w ISO C ++. Włączenie optymalizacji przekształci te zawinięte pętle w nieskończone pętle, chyba że skompilujesz z gcc / clang, -fwrapvaby dobrze podpisać przepełnienie liczb całkowitych -definiowane zachowanie: objęcie dopełniacza 2.
Ciekawostka: ISO C ++ std::atomic<int>ma dobrze zdefiniowane dopełnienie komplementu 2! int32_tmusi być uzupełnieniem 2, jeśli w ogóle jest zdefiniowane, ale zachowanie przepełnienia jest niezdefiniowane, więc nadal może być typem zdefiniowanym dla intlub longna dowolnym komputerze, na którym jeden z tych typów ma 32 bity, bez dopełniania i uzupełnienia 2).
Nieprzydatne w tym konkretnym przypadku:
Możesz także zainicjować nową zmienną jako kopię istniejącej, za pomocą nawiasów klamrowych lub (z niepustym inicjatorem) parens do bezpośredniej inicjalizacji .
int a(b)lub int a{b}są równoważne zint a = b;
Ale int b();deklaruje funkcję zamiast zmiennej zainicjowanej na zero.
Można także uzyskać zero za pomocą int()lub char(), tj. Inicjalizację zera anonimowego obiektu.
Możemy zamienić twoje <=porównania na <porównania przez prostą transformację logiczną : wykonaj przyrost licznika pętli bezpośrednio po porównaniu, zamiast na dole pętli. IMO jest to prostsze niż alternatywy proponowane przez ludzi, takie jak użycie ++w pierwszej części a, for()aby zamienić 0 na 1.
// comments aren't intended as part of the final golfed version
int n;
std::cin >> n; // end condition
for(int r{}; r < n;) { // r = rows from 0 .. n-1
++r;
for(int i{}; i < r;) {
++i;
std::cout << i << ' ';
}
std::cout << std::endl;
}
Moglibyśmy grać w golfa, for(int r{}; r++ < n;)ale IMO jest trudniejsze do odczytania dla ludzi. Nie optymalizujemy całkowitej liczby bajtów.
Gdybyśmy już używali h, moglibyśmy zaoszczędzić miejsce 'lub ".
Zakładając środowisko ASCII lub UTF-8, przestrzeń ma charwartość 32. Możemy ją łatwo utworzyć w zmiennej, a następniecout << c;
char c{};
c++; c++; // c=2
char cc(c+c+c+c); // cc=8
char s(cc+cc+cc+cc); // s=32 = ' ' = space in ASCII/UTF-8
A inne wartości można oczywiście utworzyć z sekwencji ++podwojenia i na podstawie bitów ich reprezentacji binarnej. Skutecznie przesuwając 0 (nic) lub 1 (++) do LSB przed podwojeniem do nowej zmiennej.
Ta wersja używa hzamiast 'lub ".
Jest znacznie szybszy niż którakolwiek z istniejących wersji (nie opiera się na długiej pętli) i jest wolny od niezdefiniowanego zachowania . To kompiluje bez ostrzeżenia ze g++ -O3 -Wall -Wextra -Wpedanticizclang++ . -std=c++11jest opcjonalne. Jest to legalne i przenośne ISO C ++ 11 :)
Nie opiera się również na zmiennych globalnych. I uczyniłem go bardziej czytelnym dla człowieka dzięki nazwom zmiennych, które mają znaczenie.
Liczba bajtów unikalnych: 25 , z wyłączeniem komentarzy, które usunąłemg++ -E . I wykluczając spację i znak nowej linii, jak twój licznik. Użyłem sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic tego askubuntu, aby policzyć wystąpienia każdej postaci, i wcprzeliczyłem to, aby policzyć, ile unikalnych postaci miałem.
#include<iostream>
int main() {
char c{};
c++; c++; // c=2
char cc(c+c+c+c); // cc=8
char s(cc+cc+cc+cc); // s=32 = ' ' = space in ASCII/UTF-8
int n;
std::cin >> n; // end condition
for(int r{}; r < n;) { // r = rows counting from 0
++r;
for(int i{}; i < r;) {
++i;
std::cout << i << s;
}
std::cout << std::endl;
}
}
Jedyne 2 fznaki pochodzą z for. whileZamiast tego moglibyśmy użyć pętli w.
Moglibyśmy przepisać pętle na styl asemblera, i < r || goto some_label;aby napisać warunkowy skok na dole pętli lub cokolwiek innego. (Ale używając orzamiast ||). Nie, to nie działa. gotojest instrukcją podobną do ifi nie może być podskładnikiem wyrażenia tak jak w Perlu. W przeciwnym razie moglibyśmy go użyć do usunięcia znaków (i ).
Możemy handlować fna gze if(stuff) goto label;zamiast for, a obie pętle zawsze uruchomić co najmniej 1 iteracji tak musielibyśmy jednej pętli oddziałem na dole tylko, jak normalny ASM do{}whilestrukturze pętli. Zakładając, że użytkownik wprowadzi liczbę całkowitą> 0 ...
Digraphs and Trigrafs
Na szczęście, trygrafy zostały usunięte od ISO C ++ 17, więc nie musimy ich używać ??>zamiast, }jeśli jesteśmy wyjątkowymi golfistami w najnowszej wersji C ++.
Ale tylko trygrafy: ISO C ++ 17 nadal ma digrafy, takie jak :>for ]i %>for} . Więc kosztem użyciu %, możemy uniknąć zarówno {a }i używać %:dla #dla oszczędności netto 2 mniej unikalnych znaków.
C ++ i ma kluczowe operatora jak notdla !operatora, albo bitordla |operatora. Za pomocą xor_eqfor ^=można zerować zmienną i xor_eq i, ale ma ona wiele znaków, których nie używałeś.
Obecne g++już domyślnie ignoruje trygrafy, nawet bez -std=gnu++17; musisz użyć, -trigraphsaby je włączyć -std=c++11lub coś w celu ścisłej zgodności ze standardem ISO, który je obejmuje.
23 unikalne bajty:
%:include<iostream>
int main() <%
int n;
std::cin >> n;
for(int r<% %>; r < n;) <%
++r;
for(int i<%%>; i < r;) <%
++i;
std::cout << i << ' ';
%>
std::cout << std::endl;
%>
%>
Wypróbuj online!
Ostateczna wersja używa ' pojedynczego cudzysłowu zamiast hlub "dla separatora spacji. Nie chciałem kopiować char c{}treści, więc je usunąłem. Drukowanie znaku jest bardziej wydajne niż drukowanie łańcucha, więc wykorzystałem to.
Histogram:
$ sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic | tee /dev/tty | wc -l
15 // newline
95 // space
11 %
2 '
3 (
3 )
4 +
9 :
10 ;
14 <
8 >
2 a
4 c
6 d
3 e
2 f
12 i
2 l
2 m
11 n
5 o
7 r
5 s
11 t
3 u
25 // total lines, including space and newline
Separator przestrzeni (wciąż nierozwiązany)
W usuniętej teraz odpowiedzi Johan Du Toit zaproponował zastosowanie specjalnie alternatywnego separatora std::ends. Jest to znak NUL char(0)i drukowany jest jako zero-szerokość na większości terminali. Tak wyglądałoby wyjście1234 , a nie 1 2 3 4. Albo gorzej, oddzielone śmieciami od wszystkiego, co nie cicho się zawali '\0'.
Jeśli możesz użyć dowolnego separatora, kiedy cyfra 0jest łatwa do utworzeniacout << some_zeroed_var . Ale nikt nie chce 10203040, to nawet gorzej niż brak separatora.
Próbowałem wymyślić sposób na utworzenie std::stringgospodarstwa" " bez użycia charlub dosłownego ciągu znaków. Może coś do tego dodać? Może z wykopem dla[] do ustawienia pierwszego bajtu na wartość 32po utworzeniu jednego o długości 1 za pomocą jednego z konstruktorów?
Johan zasugerował również std::iosfunkcję członka fill (), która zwraca bieżący znak wypełnienia. Domyślne ustawienie strumienia jest ustawione na std::basic_ios::init()i ma wartość ' '.
std::cout << i << std::cout.fill();zastępuje, << ' ';ale używa .zamiast' .
Dzięki -, możemy podjąć wskaźnik do couti wykorzystania ->fill()do wywołania funkcji użytkownika:
std::cout << (bitand std::cout)->fill(). Albo nie, my też nie korzystaliśmy b, więc równie dobrze moglibyśmy użyć& zamiast jego odpowiednika leksykalnym bitand.
Wywoływanie funkcji członka bez .lub->
Umieść go w klasie i zdefiniuj operator char() { fill(); }
// not digraphed
struct ss : std::ostream { // default = private inheritance
// ss() { init(); } // ostream's constructor calls this for us
operator char() { return fill(); }
}
Następnie ss s{}przed pętlą i std::cout << i << s;wewnątrz pętli. Świetnie, kompiluje się i działa poprawnie, ale musieliśmy użyć pi hdla operator char(), dla straty netto równej 1. Przynajmniej uniknęliśmy btworzenia funkcji członkowskich publicprzez używanie structzamiast class. (I możemy zastąpić dziedziczenie, protectedna wypadek, gdyby to kiedykolwiek pomogło).