El recuento de bytes asume la codificación ISO 8859-1.
+%`\B
¶$`:
1
Pruébalo en línea!
Solución alternativa:
+1`\B
:$`:
1
Explicación
Probablemente será más fácil de explicar según mi versión anterior, menos golfizada, y luego mostrar cómo la acorté. Solía convertir binario a decimal así:
^
,
+`,(.)
$`$1,
1
La única forma sensata de construir un número decimal en Retina es contando cosas (porque Retina tiene un par de características que le permiten imprimir un número decimal que representa una cantidad). Entonces, realmente, el único enfoque posible es convertir el binario en unario, y luego contar el número de dígitos unarios. La última línea cuenta, por lo que los primeros cuatro convierten el binario en unario.
¿Como hacemos eso? En general, para convertir de una lista de bits a un entero, inicializamos el resultado 0
y luego pasamos por los bits del más significativo al menos significativo, duplicamos el valor que ya tenemos y sumamos el bit actual. Por ejemplo, si el número binario es 1011
, realmente calcularíamos:
(((0 * 2 + 1) * 2 + 0) * 2 + 1) * 2 + 1 = 11
^ ^ ^ ^
Donde he marcado los bits individuales para mayor claridad.
El truco para hacer esto en unario es a) que duplicar simplemente significa repetir el número yb) ya que estamos contando la 1
s al final, ni siquiera necesitamos distinguir entre 0
sy 1
s en el proceso. Esto se aclarará en un segundo.
Lo que hace el programa es que primero agrega una coma al principio como marcador de la cantidad de información que ya hemos procesado:
^
,
A la izquierda del marcador, tendremos el valor que estamos acumulando (que se inicializa correctamente en la representación unaria de cero), y la derecha del valor será el siguiente bit a procesar. Ahora aplicamos la siguiente sustitución en un bucle:
,(.)
$`$1,
Solo mirando ,(.)
y $1,
, esto mueve el marcador un poco hacia la derecha cada vez. Pero también insertamos $`
, que está todo delante del marcador, es decir, el valor actual, que estamos duplicando. Estos son los pasos individuales al procesar la entrada 1011
, donde he marcado el resultado de insertar$`
encima de cada línea (está vacío para el primer paso):
,1011
1,011
_
110,11
___
1101101,1
_______
110110111011011,
Verá que hemos retenido y duplicado el cero junto con todo lo demás, pero como los estamos ignorando al final, no importa con qué frecuencia los hayamos duplicado, siempre que el número de 1
s sea correcto. Si los cuenta, hay algunos 11
de ellos, justo lo que necesitamos.
Eso deja la cuestión de cómo jugar golf a 12 bytes. La parte más cara de la versión de 18 bytes es tener que usar el marcador. El objetivo es deshacerse de eso. Realmente queremos duplicar el prefijo de cada bit, por lo que una primera idea podría ser esta:
.
$`$&
El problema es que estas sustituciones ocurren simultáneamente, por lo que el primer bit no se duplica para cada bit, sino que solo se copia una vez cada vez. Para la entrada 1011
obtendríamos (marcando el insertado $`
):
_ __ ___
1101011011
Todavía necesitamos procesar la entrada de forma recursiva para que el primer prefijo duplicado se duplique nuevamente por el segundo y así sucesivamente. Una idea es insertar marcadores en todas partes y reemplazarlos repetidamente con el prefijo:
\B
,
+%`,
¶$`
Después de reemplazar cada marcador con el prefijo por primera vez, debemos recordar dónde estaba el comienzo de la entrada, por lo que también insertamos saltos de línea y usamos la %
opción para asegurarnos de que el siguiente$`
solo recoja los elementos del salto de línea más cercano.
Esto funciona, pero aún es demasiado largo (16 bytes al contar 1
s al final). ¿Qué tal si cambiamos las cosas? Los lugares donde queremos insertar marcadores se identifican por \B
(una posición entre dos dígitos). ¿Por qué no simplemente insertamos prefijos en esas posiciones? Esto casi funciona, pero la diferencia es que en la solución anterior, en realidad eliminamos un marcador en cada sustitución, y eso es importante para que el proceso finalice. Sin embargo, \B
no son personajes sino solo posiciones, por lo que no se elimina nada. Nosotros podemos sin embargo detener la\B
de la coincidencia insertando en su lugar un carácter sin dígitos en este lugar. Eso convierte el límite sin palabras en un límite de palabras, que es el equivalente a eliminar el carácter marcador anterior. Y eso es lo que hace la solución de 12 bytes:
+%`\B
¶$`:
Solo para completar, aquí están los pasos individuales de procesamiento 1011
, con una línea vacía después de cada paso:
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
Nuevamente, encontrará que el último resultado contiene exactamente 11 1
s.
Como ejercicio para el lector, ¿puede ver cómo esto se generaliza fácilmente a otras bases (por unos pocos bytes adicionales por incremento en la base)?