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 -fwrapv
untuk 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_t
diperlukan sebagai komplemen 2's jika didefinisikan sama sekali, tetapi perilaku overflow tidak terdefinisi sehingga masih dapat menjadi typedef untuk int
atau long
pada 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 char
dengan 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 h
alih-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 -Wpedantic
dan denganclang++
. -std=c++11
adalah 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 wc
ke 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 f
karakter yang berasal for
. Kita bisa menggunakan while
loop 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 or
bukannya ||
). Tidak, itu tidak berhasil. goto
adalah pernyataan suka if
dan tidak bisa menjadi sub-komponen dari ekspresi seperti itu di Perl. Kalau tidak, kita bisa menggunakannya untuk menghapus (
dan )
karakter.
Kami bisa perdagangan f
untuk g
dengan 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{}while
struktur 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 not
untuk !
operator, atau bitor
untuk |
operator. Dengan xor_eq
untuk ^=
, 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 -trigraphs
untuk mengaktifkannya, -std=c++11
atau 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 h
atau "
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 0
mudah 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::string
holding" "
tanpa menggunakan char
atau 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::ios
fill () 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 cout
dan penggunaan ->fill()
untuk memanggil fungsi anggota:
std::cout << (bitand std::cout)->fill()
. Atau tidak, kami tidak menggunakan b
baik 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 p
dan h
untuk operator char()
, kerugian bersih 1. Setidaknya kami menghindari b
membuat fungsi anggota public
dengan menggunakan struct
alih-alih class
. (Dan kita bisa mengganti warisan dengan protected
kalau-kalau ada yang membantu).