Digraphsを使用した23のユニークなキャラクター。(25なし)。UBなし
C ++ 11ブレース初期化構文を使用しint var{};て=、およびを避けて整数をゼロにリスト初期化します0。(または、あなたの場合、globalを避けますiiii)。これにより、グローバル変数(ローカル変数とは異なり、静的にゼロに初期化されます)以外のゼロのソースが提供されます。
現在のコンパイラは、特別なオプションを有効にする必要なく、デフォルトでこの構文を受け入れます。
(整数のラップアラウンドトリックは楽しいですし、最適化してゴルフのためのokが無効になってますが、署名オーバーフローがISO C ++で未定義の動作です。あなたは、GCC /打ち鳴らすでコンパイルしない限り、最適化は、無限ループにそれらのラップアラウンドループを回します有効にする-fwrapvだけでなく、オーバーフロー符号付き整数与えます-defined behaviour:2の補数のラップアラウンド。
楽しい事実:ISO C ++にstd::atomic<int>は、明確に定義された2の補数のラップアラウンドがあります! int32_tすべてで定義されている場合、2の補数であることが要求されるが、それは依然としてのtypedefできるようにオーバーフロー動作は未定義であるintか、longこれらのタイプのいずれかが32ビット、無パディング、及び2の補数である任意のマシン上で)。
この特定の場合には役に立ちません:
新しい変数を、既存の変数のコピーとして、ブレースまたは(空でない初期化子を使用して)直接初期化のための括弧で初期化することもできます。
int a(b)またはint a{b}と同等ですint a = b;
しかしint b();、ゼロに初期化された変数の代わりに関数を宣言します。
また、int()またはchar()でゼロを取得できます。つまり、匿名オブジェクトのゼロ初期化です。
単純な論理変換により、<=比較を<比較に置き換えることができます。ループの最後ではなく、比較の直後にループカウンターをインクリメントします。IMOこれは++、aの最初の部分で使用してfor()0を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;
}
私たちはそれをゴルフに落とすことfor(int r{}; r++ < n;)ができましたが、人間にとって読みにくいIMOです。合計バイト数の最適化は行っていません。
我々はすでに使用していた場合h、我々は救うことができる'か、"スペースのため。
ASCIIまたはUTF-8環境を想定すると、スペースのchar値は32になります。変数で十分に簡単に作成できます。cout << 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
そして、他の値は++、それらのバイナリ表現のビットに基づいて、シーケンスの2倍から明らかに作成できます。0(なし)または1(++)をLSBに効果的にシフトしてから、新しい変数に二重化します。
このバージョンではh、'またはの代わりにを使用し"ます。
既存のバージョンのいずれよりもはるかに高速で(長いループに依存せず)、Undefined Behaviorがありません。これは、との警告なしでコンパイルg++ -O3 -Wall -Wextra -Wpedanticし、とclang++。 -std=c++11オプションです。それは合法で移植可能なISO C ++ 11です:)
また、グローバル変数に依存しません。そして、意味のある変数名を使用して、人間が読みやすくしました。
一意のバイト数:25、私が削除したコメントを除くg++ -E。カウンターのようなスペースと改行を除外します。sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic このaskubuntuから各キャラクターの出現回数をカウントするために使用し、それをパイプしてwc独自のキャラクターの数をカウントしました。
#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;
}
}
fからの2 文字のみforです。の用途whileがある場合は、代わりにループを使用できますw。
ループをアセンブリ言語スタイルに書き換えて、ループi < r || goto some_label;の下部などに条件付きジャンプを書き込むことができます。(ただし、のor代わりに使用||)。いいえ、それは機能しません。 gotoはのようなステートメントでifあり、Perlの場合のように式のサブコンポーネントにすることはできません。そうでなければ、それを使用して(および)文字を削除できます。
の代わりにと交換fすることができ、両方のループは常に少なくとも1回の反復を実行するため、通常のasm ループ構造のように、下部に1つのループ分岐のみが必要になります。ユーザーが0より大きい整数を入力すると仮定します...gif(stuff) goto label;fordo{}while
ダイグラフとトライグラフ
幸いなことに、ISO C ++ 17の時点で3文字表記が削除されたため、最新のC ++リビジョンでユニークゴルフをしている場合の??>代わりに使用する必要はありません}。
しかし、特にトライグラフのみ:ISO C ++ 17には、:>for ]や%>forのようなダイグラフがまだあります}。したがって%、を使用するコストで、との両方{を回避 し}、一意の文字を2つ少なくする%:ため#に使用できます。
また、C ++にはnot、!演算子または演算子のような演算子キーワードbitorがあり|ます。xor_eqforを使用すると^=、を使用して変数をゼロにすることができi xor_eq iますが、使用していない複数の文字が含まれています。
現在でg++は、デフォルトで3文字表記は無視されます-std=gnu++17。-trigraphsそれらを有効にするために使用する必要があります。または-std=c++11、それらを含むISO標準に厳密に準拠するために何かを使用する必要があります。
23個の一意のバイト:
%: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;
%>
%>
オンラインでお試しください!
最終バージョンでは、スペース区切り文字の'代わりに、hまたは"スペース区切り文字に単一引用符が使用されます。char c{}ものをダイグラフにしたくなかったので削除しました。文字の印刷は文字列の印刷よりも効率的であるため、それを使用しました。
ヒストグラム:
$ 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
スペース区切り文字(未解決)
削除された回答で、Johan Du Toitは代替セパレーター、特にを使用することを提案しましたstd::ends。これはNUL文字でありchar(0)、ほとんどの端末でゼロ幅として出力されます。したがって、出力は次のよう1234になります1 2 3 4。さらに悪いことに、静かに崩壊しなかったものはゴミで区切られてい'\0'ます。
任意のセパレータを使用できる場合、数字を0で簡単に作成できますcout << some_zeroed_var。しかし、誰も望ん10203040でいない、それはセパレータなしよりもさらに悪いことです。
私は、文字列リテラルを使用せずに保持を作成するstd::string" "方法を考えていましたchar。たぶん何かを追加しますか?たぶん、コンストラクタの1つを使用して長さ1の[]バイトを32作成した後、最初のバイトを値に設定するためのdigraph がありますか?
Johan は、現在の塗りつぶし文字を返すstd::iosfill()メンバー関数も提案しました。ストリームのデフォルトはによって設定されstd::basic_ios::init()、です' '。
std::cout << i << std::cout.fill();置き換えます<< ' ';が、の.代わりに使用します'。
では-、私たちはへのポインタを取ることができcout、使用する->fill()メンバ関数を呼び出すために:
std::cout << (bitand std::cout)->fill()。または、私たちはbどちらも使用していなかったので&、レキシカルな同等物の代わりに使用したかもしれませんbitand。
なしメンバ関数を呼び出します.か、->
クラス内に配置して、定義します 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(); }
}
次にss s{}、ループの前、ループのstd::cout << i << s;内側。グレート、それはコンパイルし、正常に動作し、私たちが使用していたpとhのためにoperator char()、少なくとも1の純損失のために、我々は避けbメンバ関数を作るためにpublic使用してstructの代わりにclass。(そして、私たちはprotected、これが助けになる場合に、継承をオーバーライドできます)。