23 caracteres únicos usando Digraphs. (25 sem). Sem UB.
Use a sintaxe do inicializador em C ++ 11 para inicializar em lista um número inteiro para zero, int var{};evitando =e 0. (Ou no seu caso, evitando global iiii). Isso fornece uma fonte de zeros diferentes de variáveis globais (que são inicializadas estaticamente para zero, diferentemente dos locais).
Os compiladores atuais aceitam essa sintaxe por padrão, sem precisar habilitar nenhuma opção especial.
(O truque envolvente de número inteiro é divertido e aceitável para jogar golfe com a otimização desativada, mas o estouro assinado é um comportamento indefinido no ISO C ++. A ativação da otimização transformará esses loops em envolventes em loops infinitos, a menos que você compile com gcc / clang -fwrapvpara fornecer um estouro inteiro assinado comportamento definido: complemento do 2 envolvente.
Curiosidade: o ISO C ++ std::atomic<int>possui um complemento bem definido do 2! int32_té necessário que seja o complemento de 2, se definido, mas o comportamento de estouro é indefinido, portanto ainda pode ser um typedef para intou longem qualquer máquina em que um desses tipos seja de 32 bits, sem preenchimento e o complemento de 2).
Não é útil para este caso específico:
Você também pode inicializar uma nova variável como uma cópia de uma existente, com chaves ou (com um inicializador não vazio), parens para inicialização direta .
int a(b)ou int a{b}são equivalentes aint a = b;
Mas int b();declara uma função em vez de uma variável inicializada como zero.
Além disso, você pode obter um zero com int()ou char(), ou seja, com a inicialização zero de um objeto anônimo.
Podemos substituir suas <=comparações por <comparadas por uma transformação lógica simples : faça o incremento do contador de loop logo após a comparação, em vez de na parte inferior do loop. Na IMO, isso é mais simples do que as alternativas que as pessoas propuseram, como usar ++na primeira parte de a for()para transformar 0 em 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;
}
Poderíamos jogar isso até a for(int r{}; r++ < n;)IMO, mas é menos fácil para os humanos lerem. Não estamos otimizando a contagem total de bytes.
Se já estivéssemos usando h, poderíamos salvar o espaço 'ou "um espaço.
Assumindo um ambiente ASCII ou UTF-8, o espaço é um charcom valor 32. Podemos criar isso em uma variável com bastante facilidade e, em seguida,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
E outros valores podem obviamente ser criados a partir de uma sequência ++e duplicação, com base nos bits de sua representação binária. Mudando efetivamente um 0 (nada) ou 1 (++) para o LSB antes de dobrar para uma nova variável.
Esta versão usa em hvez de 'ou ".
É muito mais rápido que qualquer uma das versões existentes (sem depender de um loop longo) e está livre de comportamento indefinido . Ele compila sem advertências com g++ -O3 -Wall -Wextra -Wpedantice comclang++ . -std=c++11é opcional. É ISO C ++ 11 legal e portátil :)
Também não depende de variáveis globais. E tornei mais legível para humanos com nomes de variáveis que têm um significado.
Contagem de bytes únicos: 25 , excluindo os comentários com os quais eu removig++ -E . E excluindo espaço e nova linha como seu contador. Eu usei sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic neste askubuntu para contar ocorrências de cada caractere e canalizei isso wcpara contar quantos caracteres únicos eu tinha.
#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;
}
}
Os únicos 2 fcaracteres são de for. Em whilevez disso, poderíamos usar loops se tivéssemos um uso w.
Poderíamos reescrever os loops em um estilo de linguagem assembly i < r || goto some_label;para escrever um salto condicional na parte inferior do loop, ou o que seja. (Mas usando em orvez de ||). Não, isso não funciona. gotoé uma declaração como ife não pode ser um subcomponente de uma expressão como no Perl. Caso contrário, poderíamos usá-lo para remover os caracteres (e ).
Poderíamos negociar fpara gcom if(stuff) goto label;em vez de for, e ambos os loops sempre executar pelo menos 1 iteração de modo que só iria precisar de um loop-filial na parte inferior, como uma normal, asm do{}whileestrutura de loop. Supondo que o usuário insira um número inteiro> 0 ...
Digraphs e Trigraphs
Felizmente, os trigrafos foram removidos a partir da ISO C ++ 17, portanto, não precisamos usá-lo em ??>vez de }jogar golfe exclusivo para a revisão mais recente do C ++.
Mas apenas trigramas especificamente: a ISO C ++ 17 ainda possui dígrafos como :>para ]e %>para} . Portanto, com o custo de uso %, podemos evitar ambos {e }, e usá- %:los #para uma economia líquida de menos 2 caracteres únicos.
E o C ++ possui palavras-chave notdo !operador, como para o operador ou bitorpara o |operador. Com xor_eqfor ^=, você pode zerar uma variável com i xor_eq i, mas ela possui vários caracteres que não estava usando.
A corrente g++já ignora trigramas por padrão, mesmo sem -std=gnu++17; você precisa usá -trigraphs-los para habilitá-los, ou -std=c++11algo para uma estrita conformidade com um padrão ISO que os inclui.
23 bytes únicos:
%: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;
%>
%>
Experimente online!
A versão final usa 'aspas simples em vez de hou "para o separador de espaço. Eu não queria digrafar as char c{}coisas, então as apaguei. Imprimir um char é mais eficiente do que imprimir uma string, então usei isso.
Histograma:
$ 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
O separador de espaço (ainda não resolvido)
Em uma resposta agora excluída, Johan Du Toit propôs o uso de um separador alternativo, especificamente std::ends. Esse é um caractere NUL char(0), e é impresso como largura zero na maioria dos terminais. Portanto, a saída seria semelhante 1234, não 1 2 3 4. Ou pior, separados por lixo em qualquer coisa que não entrou em colapso silenciosamente '\0'.
Se você pode usar um separador arbitrário, quando 0é fácil criar o dígito cout << some_zeroed_var. Mas ninguém quer 10203040, isso é ainda pior do que nenhum separador.
Eu estava tentando pensar em uma maneira de criar um std::stringholding a" " sem usar charou uma string literal. Talvez acrescentando algo a isso? Talvez com um dígrafo para []definir o primeiro byte como um valor de 32, depois de criar um com comprimento 1 por meio de um dos construtores?
Johan também sugeriu a std::iosfunção de membro fill () que retorna o caractere de preenchimento atual. O padrão para um fluxo é definido por std::basic_ios::init()e é ' '.
std::cout << i << std::cout.fill();substitui << ' ';mas usa em .vez de' .
Com -, podemos tomar um ponteiro para coute uso ->fill()para chamar a função de membro:
std::cout << (bitand std::cout)->fill(). Ou não, nós também não estávamos usando b, então poderíamos ter usado em &vez de seu equivalente lexical bitand.
Chamar uma função de membro sem .ou->
Coloque-o dentro de uma classe e defina 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(); }
}
Depois, ss s{}antes do loop, e std::cout << i << s;dentro do loop. Ótimo, ele compila e funciona corretamente, mas tivemos que usar pe hpara operator char(), com uma perda líquida de 1. Pelo menos, evitamos bcriar funções de membro publicusando em structvez de class. (E podemos substituir a herança protectedcaso isso ajude).