No está mal para un tarpit de Turing bastante detallado ...
N
Count i while _%128-9 {
Count x while _/128%2 {
Write 40
_+128
}
Write _%128
_+128-_%128+N
}
Count j while _/256-j {
Write 41
}
(Sí, todo ese espacio en blanco es obligatorio).
Nota: debido a las limitaciones de entrada de Acc !! , es imposible leer una cadena arbitraria de caracteres sin algún delimitador final. Por lo tanto, este programa espera una entrada (en stdin) como una cadena seguida de un carácter de tabulación.
Acc !! ?
Es un lenguaje que creé que solo parece inutilizable . El único tipo de datos son los enteros, la única construcción de flujo de control es el Count x while y
bucle, y la única forma de almacenar datos es un único acumulador _
. La entrada y la salida se realizan un carácter a la vez, utilizando el valor especial N
y la Write
declaración. A pesar de estas limitaciones, estoy bastante seguro de que Acc !! está Turing completo.
Explicación
La estrategia básica en Acc !! La programación consiste en utilizar la %
división de mods y enteros /
para particionar conceptualmente el acumulador, lo que le permite almacenar múltiples valores a la vez. En este programa, utilizamos tres de estas secciones: los siete bits de orden más bajo ( _%128
) almacenan un código ASCII desde la entrada; el siguiente bit ( _/128%2
) almacena un valor de bandera; y los bits restantes ( _/256
) cuentan el número de parentescos cercanos que necesitaremos.
Entrada en Acc !! proviene del valor especial N
, que lee un solo carácter y evalúa su código ASCII. Cualquier declaración que consista únicamente en una expresión asigna el resultado de esa expresión al acumulador. Entonces, comenzamos almacenando el código del primer personaje en el acumulador.
_%128
almacenará el personaje leído más recientemente. Entonces, el primer ciclo se ejecuta mientras _%128-9
no es cero, es decir, hasta que el carácter actual sea una pestaña.
Dentro del bucle, queremos imprimir a (
menos que estemos en la primera iteración. Desde Acc !! no tiene una declaración if, tenemos que usar bucles para condicionales. Usamos el bit del acumulador de 128 _/128%2
, como un valor de bandera. En la primera pasada, lo único en el acumulador es un valor ASCII <128, por lo que el indicador es 0 y se omite el bucle. En cada pase posterior, nos aseguraremos de que la bandera sea 1.
Dentro del Count x
bucle (siempre que la bandera sea 1), escribimos un par abierto (ASCII 40
) y agregamos 128 al acumulador, estableciendo así la bandera en 0 y saliendo del bucle. Esto también aumenta el valor de _/256
, que usaremos como nuestro recuento de parentescos cercanos para la salida.
Independientemente del valor de la bandera, escribimos el carácter de entrada más reciente, que es simplemente _%128
.
La siguiente asignación ( _+128-_%128+N
) hace dos cosas. Primero, al agregar 128, establece la bandera para la próxima vez a través del ciclo. En segundo lugar, pone a cero la _%128
ranura, lee otro personaje y lo almacena allí. Luego hacemos un bucle.
Cuando se Count i
cierra el ciclo, acabamos de leer un carácter de tabulación y el valor del acumulador se desglosa así:
_%128
: 9
(el carácter de tabulación)
_/128%2
: 1
(la bandera)
_/256
: número de caracteres leídos, menos 1
(El menos 1 se debe a que solo agregamos 128 al acumulador una vez durante la primera pasada a través del bucle principal). Todo lo que necesitamos ahora son los parentescos cercanos. Count j while _/256-j
bucles _/256
veces, escribiendo un close-paren (ASCII 41
) cada vez. Voila!