23 einzigartige Charaktere mit Digraphen. (25 ohne). Nein UB.
Verwenden Sie die geschweifte C ++ 11 -Initialisierungssyntax, um eine Ganzzahl unter int var{};
Vermeidung von =
und auf Null zu setzen 0
. (Oder in Ihrem Fall global vermeiden iiii
). Dies gibt Ihnen eine Quelle für andere Nullen als globale Variablen (die im Gegensatz zu lokalen Variablen statisch auf Null initialisiert sind).
Aktuelle Compiler akzeptieren diese Syntax standardmäßig, ohne dass spezielle Optionen aktiviert werden müssen.
(Der Integer-Wraparound-Trick macht Spaß und ist für Golfer mit deaktivierter Optimierung in Ordnung, aber signierter Überlauf ist undefiniertes Verhalten in ISO C ++. Durch Aktivieren der Optimierung werden diese Wraparound-Schleifen in Endlosschleifen umgewandelt, es sei denn, Sie kompilieren mit gcc / clang -fwrapv
, um einen signierten Integer-Überlauf zu erzielen -definiertes Verhalten: Komplementumlauf von 2.
Unterhaltsame Tatsache: ISO C ++ std::atomic<int>
hat einen gut definierten 2er-Komplement-Wrap-Around! int32_t
Es muss das Zweierkomplement sein, wenn es überhaupt definiert ist, aber das Überlaufverhalten ist undefiniert, sodass es weiterhin ein typedef für int
oder long
auf jedem Computer sein kann, auf dem einer dieser Typen 32 Bit, kein Auffüllen und das Zweierkomplement ist.)
Für diesen speziellen Fall nicht nützlich:
Sie können eine neue Variable auch mit geschweiften Klammern oder (mit einem nicht leeren Initialisierer) mit Parens für die direkte Initialisierung als Kopie einer vorhandenen Variablen initialisieren .
int a(b)
oder int a{b}
sind äquivalent zuint a = b;
Aber int b();
deklariert eine Funktion statt einer Variable auf Null initialisiert.
Sie können auch eine Null mit int()
oder erhalten char()
, dh eine Null-Initialisierung eines anonymen Objekts.
Wir können Ihre <=
Vergleiche durch <
Vergleiche durch eine einfache logische Transformation ersetzen : Führen Sie das Inkrement des Schleifenzählers direkt nach dem Vergleich durch, anstatt am Ende der Schleife. IMO ist dies einfacher als die Alternativen, die die Leute vorgeschlagen haben, wie ++
im ersten Teil von a for()
, um aus einer 0 eine 1 zu machen.
// 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;
}
Wir könnten Golf spielen, for(int r{}; r++ < n;)
aber IMO ist das für Menschen weniger einfach zu lesen. Wir optimieren nicht für die Gesamtanzahl der Bytes.
Wenn wir bereits verwenden h
, könnten wir das '
oder "
für ein Leerzeichen speichern .
Angenommen, eine ASCII- oder UTF-8-Umgebung hat das Leerzeichen den char
Wert 32. Das können wir dann leicht in einer Variablen erstellencout << 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
Und andere Werte können offensichtlich aus einer Folge von ++
und Verdopplung erzeugt werden, basierend auf den Bits ihrer Binärdarstellung. Verschieben Sie effektiv eine 0 (nichts) oder 1 (++) in das LSB, bevor Sie es in eine neue Variable verdoppeln.
Diese Version verwendet h
anstelle von '
oder "
.
Es ist viel schneller als jede der vorhandenen Versionen (ohne auf eine lange Schleife angewiesen zu sein) und es ist frei von undefiniertem Verhalten . Es kompiliert ohne Warnungen mit g++ -O3 -Wall -Wextra -Wpedantic
und mitclang++
. -std=c++11
es ist optional. Es ist legal und portabel ISO C ++ 11 :)
Es ist auch nicht auf globale Variablen angewiesen. Und ich habe es mit Variablennamen, die eine Bedeutung haben, lesbarer gemacht.
Anzahl der eindeutigen Bytes: 25 , ohne die Kommentare, die ich entfernt habeg++ -E
. Und ohne Leerzeichen und Zeilenvorschub wie bei Ihrem Counter. Ich habe sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic
dieses Askubuntu verwendet, um die Vorkommen der einzelnen Charaktere zu zählen, und habe es weitergeleitet, um wc
zu zählen, wie viele eindeutige Charaktere ich hatte.
#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;
}
}
Die einzigen 2 f
Zeichen sind von for
. Wir könnten while
stattdessen Schleifen verwenden, wenn wir eine Verwendung für hatten w
.
Wir könnten die Schleifen möglicherweise in einen Assembler-Stil umschreiben i < r || goto some_label;
, um einen bedingten Sprung am Ende der Schleife zu schreiben, oder was auch immer. (Aber mit or
statt ||
). Nein, das geht nicht. goto
ist eine Anweisung wie if
in Perl und kann keine Unterkomponente eines Ausdrucks sein. Andernfalls hätten wir es verwenden können, um die Zeichen (
und zu entfernen )
.
Wir konnten den Handel f
für g
mit if(stuff) goto label;
statt for
, und beide Schleifen immer mindestens 1 Iteration laufen , so dass wir nur an der Unterseite eine Schleife-Zweig benötigen würde, wie eine normale asm do{}while
Schleifenstruktur. Angenommen, der Benutzer gibt eine Ganzzahl> 0 ein ...
Digraphen und Trigraphen
Glücklicherweise wurden Trigraphen ab ISO C ++ 17 entfernt, sodass wir sie nicht mehr verwenden müssen, ??>
anstatt }
für die letzte C ++ - Revision ausschließlich Golf zu spielen.
Aber nur Trigraphen im Speziellen: ISO C ++ 17 hat immer noch Digraphen wie :>
für ]
und %>
für}
. Also auf Kosten der Verwendung %
, können wir beide vermeiden {
und }
, und verwenden Sie %:
für #
eine Netto-Einsparung von 2 weniger eindeutigen Zeichen.
Und C ++ hat Operator-Schlüsselwörter wie not
für den !
Operator oder bitor
für den |
Operator. Mit xor_eq
for ^=
können Sie eine Variable auf Null setzen i xor_eq i
, sie enthält jedoch mehrere Zeichen, die Sie nicht verwendet haben.
Current g++
ignoriert Trigraphen bereits standardmäßig, auch ohne -std=gnu++17
; Sie müssen verwenden -trigraphs
, um sie zu aktivieren, oder -std=c++11
etwas, um einer ISO-Norm, die sie enthält, strikt zu entsprechen.
23 eindeutige Bytes:
%: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;
%>
%>
Probieren Sie es online!
In der endgültigen Version wird '
anstelle von h
oder "
für das Leerzeichen ein einfaches Anführungszeichen verwendet. Ich wollte das char c{}
Zeug nicht digraphen, also habe ich es gelöscht. Das Drucken eines Zeichens ist effizienter als das Drucken einer Zeichenfolge, also habe ich das verwendet.
Histogramm:
$ 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
Das Leerzeichen (noch ungelöst)
In einer jetzt gelöschten Antwort schlug Johan Du Toit die Verwendung eines alternativen Trennzeichens vor std::ends
. Das ist ein NUL-Zeichen char(0)
und wird auf den meisten Terminals als Breite Null gedruckt. So würde die Ausgabe aussieht 1234
, nicht 1 2 3 4
. Oder schlimmer noch, getrennt durch Müll an irgendetwas, das nicht lautlos zusammenbrach '\0'
.
Wenn Sie ein beliebiges Trennzeichen verwenden können, können Sie die Ziffer 0
einfach mit erstellen cout << some_zeroed_var
. Aber niemand will 10203040
, das ist noch schlimmer als kein Trennzeichen.
Ich habe versucht, einen Weg zu finden, um eine std::string
Holding" "
ohne Verwendung char
eines String-Literal zu erstellen . Vielleicht etwas anhängen? Vielleicht mit einem Digraphen []
, um das erste Byte auf einen Wert von zu setzen 32
, nachdem über einen der Konstruktoren eines mit der Länge 1 erstellt wurde?
Johan schlug auch die std::ios
Mitgliedsfunktion fill () vor, die das aktuelle Füllzeichen zurückgibt. Die Standardeinstellung für einen Stream ist std::basic_ios::init()
und ' '
.
std::cout << i << std::cout.fill();
ersetzt << ' ';
aber verwendet .
statt'
.
Mit -
, können wir einen Zeiger nehmen cout
und die Verwendung ->fill()
der Memberfunktion aufzurufen:
std::cout << (bitand std::cout)->fill()
. Oder auch nicht, wir wurden mit nicht b
entweder so können wir auch genutzt haben &
statt dessen lexikalische Äquivalent bitand
.
Aufrufen einer Mitgliedsfunktion ohne .
oder->
Setzen Sie es in eine Klasse und definieren Sie 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(); }
}
Dann ss s{}
vor der Schleife und std::cout << i << s;
innerhalb der Schleife. Großartig, es kompiliert und funktioniert einwandfrei , aber wir mussten Gebrauch p
und h
für operator char()
, für einen Nettoverlust von 1. Mindestens wir vermieden b
Mitgliederfunktionen zu machen , public
indem Sie struct
statt class
. (Und wir könnten die Vererbung mit überschreiben, protected
falls das jemals hilft).