23 уникальных персонажа, использующих Digraphs. (25 без). Нет UB.
Используйте синтаксис скобочных инициализаторов 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(), то есть инициализацию нуля анонимного объекта.
Мы можем заменить ваши <=сравнения <сравнениями простым логическим преобразованием : сделайте приращение счетчика цикла сразу после сравнения, а не внизу цикла. 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
И другие значения, очевидно, могут быть созданы из последовательности ++ и удвоения, основываясь на битах их двоичного представления. Эффективно сдвигая 0 (ничего) или 1 (++) в LSB, прежде чем удвоить в новую переменную.
Эта версия использует h вместо 'или ".
Это намного быстрее, чем любая из существующих версий (не полагаясь на длинный цикл), и не содержит неопределенного поведения . Он компилируется без предупреждений с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 этот аскубунту для подсчета вхождений каждого персонажа, и 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 итерации , поэтому мы должны были бы только одну петлю-ветви в нижней части, как обычный ассемблере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
Разделитель пространства (до сих пор не решен)
В уже удаленном ответе Johan Du Toit предложил использовать, в частности, альтернативный разделитель std::ends. Это символ NUL char(0), и он печатается как нулевая ширина на большинстве терминалов. Таким образом, результат будет выглядеть 1234, а не 1 2 3 4. Или, что еще хуже, отделенный мусором от всего, что не рухнуло'\0' .
Если вы можете использовать произвольный разделитель, когда цифру 0легко создать cout << some_zeroed_var. Но никто не хочет 10203040, это даже хуже, чем без разделителя.
Я пытался придумать способ создания std::stringхолдинга" " без использования charстрокового литерала. Может быть, что-то добавить к этому? Может быть, с орграфом для []установки первого байта в значение 32после создания единицы длиной 1 через один из конструкторов?
Йохан также предложил 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в случае, если это когда-нибудь поможет).