Haskell , 1080 1033 bytes
;
f=
g
ij=f
a =hi
hi = g
hij= ij
g ' ' =0
g '"' =0;
g '$' =0;
g '&' =0-0
g '(' =0-0-0
g '*' =0-0-0;
g ',' =0-0-0;
g '.' =0-0-0-0
g '0' =0-0-0-0-0
g '2' =0-0-0-0-0;
g '4' =0-0-0-0-0;
g '6' =0; g '8' =0
g ':' =0; g '<' =0-0
g '>' =0; g '@' =0-0;
g 'B' =0; g 'D' =0-0;
g 'F' =0; g 'H' =0-0-0
g 'J' =0; g 'L' =0-0-0-0
g 'N' =0; g 'P' =0-0-0-0;
g 'R' =0; g 'T' =0-0-0-0;
g 'V' =0; g 'X' =0-0-0-0-0
g 'Z' =0; g '^' =0; g '`' =0
g 'b' =0; g 'd' =0; g 'f' =0;
g 'h' =0; g 'j' =0; g 'l' =0;
g 'n' =0; g 'p' =0; g 'r' =0-0
g 't' =0; g 'v' =0; g 'x' =0-0-0
g 'z' =0; g '\92' =0-0; g '|' =0;
g '~' =0; g y = 1 ;z=0; i(-0)z=z;
i m('\10':y ) ="y"; ; ; ; ; ; ; ;
i m(mnmnmnmnm:y ) = i(m - 1 ) y ; ;
i k m ="y"; ; k i [ ] =01<1010101010;
k m('\10':y ) = k(m + 1 )(i m y ) ; ;
k m y =01>10; m o = k 1$'\10':o ; ; ;
o i('\10':y ) = o i y ; ; ; ; ; ; ; ; ;
o i(k:y )|g k<i = o(1 - i ) y ; ; ; ; ; ;
o i(k:y )|g k>i = o(1 - i ) y ; ; ; ; ; ;
o i [ ] =01<10; o i y =01>10;v=01>10101010
s y|o 1 y = m y|o(-0) y = m y ; s y =v; ; ;
Experimente online!
Explicação
Essa tem sido uma tarefa bastante interessante para Haskell.
Paridade
Para começar, precisamos de uma maneira de determinar se um personagem tem um ponto de código par ou ímpar. A maneira normal de fazer isso é obter o ponto de código e modificá-lo com 2. No entanto, como se pode saber, obter o ponto de código de um caracter exige uma importação, o que devido à restrição de origem significa que ele não pode ser usava. Um Haskeller mais experiente pensaria em usar recursão. Char
fazem parte da Enum
classe para que possamos obter seus antecessores e sucessores. No entanto pred
e succ
também são ambos inutilizável porque eles não fazem paridade byte alternativo.
Então, isso nos deixa muito presos, praticamente não podemos fazer nenhuma manipulação com caracteres. A solução para isso é codificar tudo. Podemos representar (a maioria) caracteres pares como literais, probabilidades com as quais temos problemas porque '
é ímpar, por isso não pode estar ao lado do caractere, tornando o literal impossível de expressar a maioria dos caracteres ímpares. Portanto, codificamos todos os bytes pares e adicionamos uma captura de todos os bytes ímpares no final.
Bytes com problema
Você pode perceber que existem alguns bytes pares para os quais literais não podem ser feitos colocando-os entre aspas simples. Eles são os imprimíveis, as novas linhas e \
. Não precisamos nos preocupar com imprimíveis, desde que não utilizemos nenhum deles, não precisamos verificar. De fato, ainda podemos usar estranhos não imprimíveis, como tab, simplesmente não preciso. A nova linha pode sabiamente ser ignorada, pois será cortada do programa de qualquer maneira. (Poderíamos incluir nova linha, porque seu ponto de código é bastante conveniente, mas não precisamos). Isso deixa \
, agora \
tem o ponto de código 92, que convenientemente é um número ímpar seguido por um número par, então \92
alterna entre pares e probabilidades, portanto, o'\92'
é perfeitamente válido. Mais tarde, quando precisarmos representar a nova linha, perceberemos que ela possui essa mesma propriedade '\10'
.
Problemas de espaçamento
Agora, para começar a escrever o código real, precisamos poder colocar um número considerável de caracteres em uma única linha. Para fazer isso, escrevi o cap:
;
f=
g
ij=f
a =hi
hi = g
hij= ij
A tampa não faz nada, exceto ser Haskell válido. Inicialmente, eu esperava fazer definições que nos ajudassem no código mais tarde, mas isso não aconteceu. Também existem maneiras mais fáceis de fazer o limite, por exemplo, espaço em branco e ponto-e-vírgula, mas eles não salvam bytes por esse caminho, então não me preocupei em alterá-lo.
Hardcoder
Então agora que tenho espaço suficiente em uma linha, inicio a codificação dos valores. Isso é muito chato, mas há algumas coisas interessantes. Por uma vez, as linhas começam a ficar ainda mais longas, podemos usar ;
para colocar várias declarações em uma linha, o que nos economiza uma tonelada de bytes.
A segunda é que, como nem sempre podemos iniciar uma linha de g
vez em quando, precisamos recuar um pouco as linhas. Agora, Haskell realmente se importa com o recuo, por isso vai reclamar disso. No entanto, se a última linha antes da linha recuada terminar em ponto e vírgula, será permitida. Por quê? Eu não tenho o menor, mas funciona. Então, precisamos nos lembrar de colocar o ponto e vírgula no final das linhas.
Blocos de Construção de Função
Uma vez feito o codificador, é fácil navegar até o final do programa. Precisamos construir algumas funções simples. Primeiro eu construo uma versão do drop
chamado i
. i
é diferente do drop
que, se tentarmos ultrapassar o final da string, ela retornará "y"
. i
é diferente de drop também, pois, se tentar soltar uma nova linha, retornará "y"
. Isso será útil porque mais tarde, quando verificarmos que o programa é um triângulo, isso nos permitirá retornar False
quando a última linha não estiver concluída ou quando uma linha termina cedo.
k
k
nssTrue
nk
n + 1False
Em seguida, fazer um alias para k
, m
. m
é apenas k
com 1
no primeiro argumento e uma nova linha é anexada ao segundo argumento.
Em seguida, temos o
. o
pega um número e uma string. Determina se os bytes da string (ignorando novas linhas) se alternam em paridade (usando nossa g
) começando com o número de entrada.
Por fim, temos o s
que roda o
com ambos 1
e 0
, se um deles é bem-sucedido, ele adia m
. Se falhar, apenas retorna False
. Esta é a função que queremos. Determina que a entrada é triangular e alternada.