23 унікальні символи за допомогою Digraphs. (25 без). Немає УБ.
Використовуйте синтаксис ініціалізатора, уповноваженого C ++ 11, для списку-ініціалізації цілого числа до нуля, int var{};уникаючи =та 0. (Або у вашому випадку, уникаючи глобальних iiii). Це дає вам джерело нулів, окрім глобальних змінних (які статично ініціалізовані до нуля, на відміну від локальних).
Поточні компілятори приймають цей синтаксис за замовчуванням, не вмикаючи жодних спеціальних параметрів.
(Цілий фокус накрутки - це весело, і нормально для гольфу з вимкненою оптимізацією, але підписане переповнення не визначене поведінка в ISO C ++. Увімкнення оптимізації перетворить ці петлі накрутки в нескінченні петлі, якщо ви не компілюєте з gcc / clang, -fwrapvщоб дати підписаному цілому цілому переповнення добре -визначена поведінка: обробка комплексу 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(), тобто нуль-ініціалізації анонімного об'єкта.
Ми можемо замінити ваші <=порівняння зі <порівняннями простим логічним перетворенням : зробіть приріст циклу лічильника відразу після порівняння, а не внизу циклу. ІМО це простіше, ніж запропоновані людьми альтернативи, як, наприклад, використовувати ++в першій частині а, 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;)ІМО, який людині читати менш просто. Ми не оптимізуємо загальну кількість байтів.
Якщо ми вже використовували 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
І інші значення, очевидно, можуть бути створені з послідовності ++та подвоєння, виходячи з бітів їх бінарного подання. Ефективно зміщення 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;
}
}
Тільки 2 fсимволи - від for. Ми могли б whileзамість цього використовувати петлі, якби для них використовувались w.
Ми, можливо, зможемо переписати петлі у стилі мови складання, i < r || goto some_label;щоб написати умовний стрибок у нижній частині циклу, чи будь-що інше. (Але використовуючи orзамість ||). Ні, це не працює. gotoє твердженням, як ifі не може бути підкомпонентним виразом, як він може в Perl. В іншому випадку ми могли б використовувати його для видалення символів (і ).
Ми могли б торгувати fна gз if(stuff) goto label;а for, і обидві петлі завжди виконуються по крайней мере 1 ітерації , тому ми повинні були б тільки одну петлю-гілки в нижній частині, як нормальний ASM do{}whileструктури петлі. Якщо припустити, що користувач вводить ціле число> 0 ...
Диграфи та триграфи
На щастя, триграфи були вилучені за ISO C ++ 17, тому нам не доведеться використовувати ??>замість того, }якщо ми унікальні для гольфу для останньої редакції C ++.
Але тільки тріграфи саме: ISO C ++ 17 по і раніше має диграфів як :>для ]і %>для} . Тож ціною використання %ми можемо уникнути і {і }, і використовувати %:для #чистої економії на 2 менших унікальних символи.
І C ++ має ключові слова оператора, як notдля !оператора, або bitorдля |оператора. З допомогою xor_eqfor ^=, ви можете нульову змінну i xor_eq i, але вона містить кілька символів, які ви не використовували.
Поточна g++вже ігнорує триграфи за замовчуванням навіть без -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
Розділювач простору (досі невирішений)
У видаленій відповіді Йохан Дю Тойт запропонував конкретно використовувати альтернативний роздільник std::ends. Це символ NUL char(0), і друкується як нульова ширина на більшості терміналів. Таким чином, вихід буде виглядати, як 1234ні 1 2 3 4. Або ще гірше, розділений сміттям на будь-що, що мовчки не руйнувалося '\0'.
Якщо ви можете використовувати довільний роздільник, коли цифру 0легко створити за допомогою cout << some_zeroed_var. Але ніхто не хоче 10203040, це ще гірше, ніж жоден роздільник.
Я намагався придумати спосіб створення std::stringхолдингу" " без використання charабо рядкового літералу. Можливо, до чогось додати? Може, за допомогою диграфа для []встановлення першого байта значення 32, після створення одного з довжиною 1 через один з конструкторів?
Йохан також запропонував std::iosфункцію члена (), яка повертає поточний символ заповнення. За замовчуванням для потоку встановлюється 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якщо це коли-небудь допоможе).