A contagem de bytes assume a codificação ISO 8859-1.
+%`\B
¶$`:
1
Experimente online!
Solução alternativa:
+1`\B
:$`:
1
Explicação
Provavelmente será mais fácil explicar isso com base na minha versão antiga, menos golfe, e depois mostrar como a reduzi. Eu costumava converter binário em decimal como este:
^
,
+`,(.)
$`$1,
1
A única maneira sensata de construir um número decimal no Retina é contando as coisas (porque o Retina possui alguns recursos que permitem imprimir um número decimal representando um valor). Então, realmente, a única abordagem possível é converter o binário em unário e, em seguida, contar o número de dígitos unários. A última linha faz a contagem; portanto, os quatro primeiros convertem o binário em unário.
Como fazemos isso? Em geral, para converter de uma lista de bits em um número inteiro, inicializamos o resultado 0
e depois passamos pelos bits do mais para o menos significativo, o dobro do valor que já temos e adicionamos o bit atual. Por exemplo, se o número binário for 1011
, calcularíamos realmente:
(((0 * 2 + 1) * 2 + 0) * 2 + 1) * 2 + 1 = 11
^ ^ ^ ^
Onde marquei os bits individuais para maior clareza.
O truque para fazer isso em unário é a) que dobrar significa repetir o número eb) já que estamos contando os 1
s no final, nem precisamos distinguir 0
s e 1
s no processo. Isso ficará mais claro em um segundo.
O que o programa faz é adicionar primeiro uma vírgula no início como marcador de quanto da entrada já processamos:
^
,
À esquerda do marcador, teremos o valor que estamos acumulando (que foi corretamente inicializado para a representação unária de zero) e à direita do valor será o próximo bit a ser processado. Agora aplicamos a seguinte substituição em um loop:
,(.)
$`$1,
Apenas olhando ,(.)
e $1,
, isso move o marcador um pouco para a direita a cada vez. Mas também inserimos $`
, que é tudo à frente do marcador, ou seja, o valor atual, que estamos dobrando. Aqui estão as etapas individuais ao processar a entrada 1011
, onde eu marquei o resultado da inserção $`
acima de cada linha (ela está vazia na primeira etapa):
,1011
1,011
_
110,11
___
1101101,1
_______
110110111011011,
Você verá que retivemos e dobramos o zero junto com todo o resto, mas, como os desconsideramos no final, não importa com que frequência os duplicamos, desde que o número de 1
s seja corrigir. Se você os contar, existem 11
, exatamente o que precisamos.
Portanto, isso deixa a questão de como jogar isso em golfe até 12 bytes. A parte mais cara da versão de 18 bytes é usar o marcador. O objetivo é se livrar disso. Nós realmente queremos dobrar o prefixo de cada bit, então uma primeira idéia pode ser a seguinte:
.
$`$&
O problema é que essas substituições ocorrem simultaneamente, portanto, o primeiro bit não é duplicado para cada bit, mas é copiado uma vez de cada vez. Para entrada 1011
, obteríamos (marcando o inserido $`
):
_ __ ___
1101011011
Ainda precisamos processar a entrada recursivamente para que o primeiro prefixo duplicado seja duplicado novamente pelo segundo e assim por diante. Uma idéia é inserir marcadores em todos os lugares e substituí-los repetidamente pelo prefixo:
\B
,
+%`,
¶$`
Depois de substituir cada marcador pelo prefixo pela primeira vez, precisamos lembrar onde estava o início da entrada; portanto, inserimos feeds de linha também e usamos a %
opção para garantir que o próximo $`
aponte apenas o feed de linha mais próximo.
Isso funciona, mas ainda é muito longo (16 bytes ao contar 1
s no final). Que tal mudar as coisas? Os locais onde queremos inserir marcadores são identificados por \B
(uma posição entre dois dígitos). Por que simplesmente não inserimos prefixos nessas posições? Isso quase funciona, mas a diferença é que, na solução anterior, removemos um marcador em cada substituição, e isso é importante para encerrar o processo. No entanto, o \B
personagem não é apenas uma posição, então nada é removido. No entanto, podemos parar o\B
da correspondência inserindo um caractere não dígito neste local. Isso transforma o limite de não palavra em um limite de palavra, o que equivale a remover o caractere marcador anteriormente. E é isso que a solução de 12 bytes faz:
+%`\B
¶$`:
Apenas para completar, aqui estão as etapas individuais do processamento 1011
, com uma linha vazia após cada etapa:
1
1:0
10:1
101:1
1
1:0
1
1:0:1
1
1:0
10:1:1
1
1:0
1
1:0:1
1
1:0
1
1:0:1:1
Novamente, você verá que o último resultado contém exatamente 11 1
s.
Como um exercício para o leitor, você pode ver como isso se generaliza facilmente para outras bases (por alguns bytes adicionais por incremento na base)?