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 -fwrapv
para 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 int
ou long
em 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 char
com 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 h
vez 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 -Wpedantic
e 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 wc
para 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 f
caracteres são de for
. Em while
vez 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 or
vez de ||
). Não, isso não funciona. goto
é uma declaração como if
e 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 f
para g
com 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{}while
estrutura 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 not
do !
operador, como para o operador ou bitor
para o |
operador. Com xor_eq
for ^=
, 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++11
algo 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 h
ou "
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::string
holding a" "
sem usar char
ou 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::ios
funçã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 cout
e 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 p
e h
para operator char()
, com uma perda líquida de 1. Pelo menos, evitamos b
criar funções de membro public
usando em struct
vez de class
. (E podemos substituir a herança protected
caso isso ajude).