23 karakter unik menggunakan Digraphs. (25 tanpa). Tidak ada UB
Gunakan sintaksis penyangga penguat C ++ 11 untuk membuat daftar-inisialisasi integer ke nol dengan int var{};menghindari =dan 0. (Atau dalam kasus Anda, menghindari global iiii). Ini memberi Anda sumber nol selain variabel global (yang diinisialisasi secara statis ke nol, tidak seperti penduduk lokal).
Kompiler saat ini menerima sintaks ini secara default, tanpa harus mengaktifkan opsi khusus apa pun.
(Trik sampul integer menyenangkan, dan ok untuk bermain golf dengan optimasi dinonaktifkan, tetapi limpahan yang ditandatangani adalah perilaku tidak terdefinisi dalam ISO C ++. Mengaktifkan optimasi akan mengubah loop sampul menjadi loop tak terbatas, kecuali jika Anda mengkompilasi dengan gcc / dentang -fwrapvuntuk memberikan limpahan bilangan bulat yang ditandatangani dengan baik -definisi perilaku: sampul komplemen 2's.
Fakta menyenangkan: ISO C ++ std::atomic<int>memiliki bungkus komplemen 2 yang terdefinisi dengan baik! int32_tdiperlukan sebagai komplemen 2's jika didefinisikan sama sekali, tetapi perilaku overflow tidak terdefinisi sehingga masih dapat menjadi typedef untuk intatau longpada mesin mana pun di mana salah satu tipe tersebut adalah 32 bit, tanpa padding, dan komplemen 2's.)
Tidak berguna untuk kasus khusus ini:
Anda juga dapat menginisialisasi variabel baru sebagai salinan dari yang sudah ada, dengan kurung kurawal atau (dengan inisialisasi non-kosong), parens untuk inisialisasi langsung .
int a(b)atau int a{b}setara denganint a = b;
Tetapi int b();mendeklarasikan fungsi alih-alih variabel diinisialisasi ke nol.
Juga, Anda bisa mendapatkan nol dengan int()atau char(), yaitu nol-inisialisasi objek anonim.
Kami dapat mengganti <=perbandingan Anda dengan <membandingkan dengan transformasi logika sederhana : lakukan kenaikan loop-counter tepat setelah membandingkan, bukan di bagian bawah loop. IMO ini lebih sederhana daripada alternatif yang diusulkan orang, seperti menggunakan ++di bagian pertama dari for()untuk membuat 0 menjadi 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;
}
Kita bisa bermain golf hingga for(int r{}; r++ < n;)tetapi IMO yang kurang mudah bagi manusia untuk membaca. Kami tidak mengoptimalkan jumlah byte total.
Jika kami sudah menggunakan h, kami bisa menghemat 'atau "untuk ruang.
Dengan asumsi lingkungan ASCII atau UTF-8, ruang adalah chardengan nilai 32. Kita dapat membuat itu dalam variabel dengan cukup mudah, lalucout << 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
Dan nilai-nilai lain jelas dapat dibuat dari urutan ++dan penggandaan, berdasarkan bit dari representasi biner mereka. Secara efektif menggeser 0 (tidak ada) atau 1 (++) ke dalam LSB sebelum menggandakannya menjadi variabel baru.
Versi ini menggunakan halih-alih 'atau ".
Ini jauh lebih cepat daripada versi yang ada (tidak bergantung pada loop panjang), dan bebas dari Perilaku Tidak Terdefinisi . Itu mengkompilasi tanpa peringatan dengan g++ -O3 -Wall -Wextra -Wpedanticdan denganclang++ . -std=c++11adalah opsional. Ini legal dan portabel ISO C ++ 11 :)
Itu juga tidak bergantung pada variabel global. Dan saya membuatnya lebih bisa dibaca manusia dengan nama variabel yang memiliki arti.
Hitungan byte unik: 25 , tidak termasuk komentar yang saya abaikang++ -E . Dan tidak termasuk ruang dan baris baru seperti penghitung Anda. Saya menggunakan sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic dari askubuntu ini untuk menghitung kemunculan setiap karakter, dan memasukkannya wcke dalam untuk menghitung berapa banyak karakter unik yang saya miliki.
#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;
}
}
Hanya 2 fkarakter yang berasal for. Kita bisa menggunakan whileloop sebagai gantinya jika ada gunanya w.
Kita mungkin dapat menulis ulang loop ke gaya bahasa assembly i < r || goto some_label;untuk menulis lompatan bersyarat di bagian bawah loop, atau apa pun. (Tetapi menggunakan orbukannya ||). Tidak, itu tidak berhasil. gotoadalah pernyataan suka ifdan tidak bisa menjadi sub-komponen dari ekspresi seperti itu di Perl. Kalau tidak, kita bisa menggunakannya untuk menghapus (dan )karakter.
Kami bisa perdagangan funtuk gdengan if(stuff) goto label;bukan for, dan kedua loop selalu berjalan minimal 1 iterasi sehingga kita hanya akan membutuhkan satu loop-cabang di bagian bawah, seperti asm yang normal do{}whilestruktur lingkaran. Dengan asumsi pengguna memasukkan bilangan bulat> 0 ...
Digraphs dan Trigraphs
Untungnya, trigraph telah dihapus pada ISO C ++ 17 jadi kami tidak harus menggunakan ??>alih-alih }jika kami bermain golf khusus untuk revisi C ++ terbaru.
Tetapi hanya trigraph yang spesifik: ISO C ++ 17 masih memiliki digraf seperti :>untuk ]dan %>untuk} . Jadi dengan biaya penggunaan %, kita dapat menghindari keduanya {dan }, dan menggunakannya %:untuk #penghematan bersih 2 karakter unik lebih sedikit.
Dan C ++ memiliki kata kunci operator seperti notuntuk !operator, atau bitoruntuk |operator. Dengan xor_equntuk ^=, Anda dapat nol variabel i xor_eq i, tetapi memiliki beberapa karakter yang tidak Anda gunakan.
Saat ini g++sudah mengabaikan trigraph secara default bahkan tanpa -std=gnu++17; Anda harus menggunakan -trigraphsuntuk mengaktifkannya, -std=c++11atau sesuatu untuk kesesuaian ketat dengan standar ISO yang menyertakannya.
23 byte unik:
%: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;
%>
%>
Cobalah online!
Versi terakhir menggunakan 'kutipan tunggal, bukan hatau "untuk pemisah ruang. Saya tidak ingin membuat digraf char c{}barang jadi saya menghapusnya. Mencetak char lebih efisien daripada mencetak string, jadi saya menggunakannya.
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
Pemisah ruang (masih belum terpecahkan)
Dalam jawaban yang sekarang dihapus, Johan Du Toit mengusulkan menggunakan pemisah alternatif, khususnya std::ends. Itu karakter NUL char(0),, dan mencetak sebagai lebar nol pada sebagian besar terminal. Jadi hasilnya akan seperti 1234, bukan 1 2 3 4. Atau lebih buruk, dipisahkan oleh sampah pada apa pun yang tidak runtuh diam-diam '\0'.
Jika Anda dapat menggunakan pemisah arbitrer, saat digit 0mudah dibuat cout << some_zeroed_var. Tapi tidak ada yang mau 10203040, itu bahkan lebih buruk daripada tidak ada pemisah.
Saya mencoba memikirkan cara untuk membuat std::stringholding" " tanpa menggunakan charatau string literal. Mungkin menambahkan sesuatu padanya? Mungkin dengan digraf untuk []mengatur byte pertama ke nilai 32, setelah membuat satu dengan panjang 1 melalui salah satu konstruktor?
Johan juga menyarankan fungsi std::iosfill () anggota yang mengembalikan karakter isian saat ini. Default untuk streaming ditetapkan oleh std::basic_ios::init(), dan ' '.
std::cout << i << std::cout.fill();menggantikan << ' ';tetapi menggunakan .bukan' .
Dengan -, kita dapat mengambil pointer ke coutdan penggunaan ->fill()untuk memanggil fungsi anggota:
std::cout << (bitand std::cout)->fill(). Atau tidak, kami tidak menggunakan bbaik jadi kami mungkin juga telah digunakan &sebagai pengganti setara leksikal nya, bitand.
Memanggil fungsi anggota tanpa .atau->
Letakkan di dalam kelas, dan tentukan 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(); }
}
Lalu ss s{}sebelum loop, dan std::cout << i << s;di dalam loop. Hebat, itu mengkompilasi dan bekerja dengan baik, tetapi kami harus menggunakan pdan huntuk operator char(), kerugian bersih 1. Setidaknya kami menghindari bmembuat fungsi anggota publicdengan menggunakan structalih-alih class. (Dan kita bisa mengganti warisan dengan protectedkalau-kalau ada yang membantu).