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より大きい整数を入力すると仮定します...g
if(stuff) goto label;
for
do{}while
ダイグラフとトライグラフ
幸いなことに、ISO C ++ 17の時点で3文字表記が削除されたため、最新のC ++リビジョンでユニークゴルフをしている場合の??>
代わりに使用する必要はありません}
。
しかし、特にトライグラフのみ:ISO C ++ 17には、:>
for ]
や%>
forのようなダイグラフがまだあります}
。したがって%
、を使用するコストで、との両方{
を回避 し}
、一意の文字を2つ少なくする%:
ため#
に使用できます。
また、C ++にはnot
、!
演算子または演算子のような演算子キーワードbitor
があり|
ます。xor_eq
forを使用すると^=
、を使用して変数をゼロにすることができ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::ios
fill()メンバー関数も提案しました。ストリームのデフォルトはによって設定され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
、これが助けになる場合に、継承をオーバーライドできます)。