¡Diseñe una computadora con un conjunto de instrucciones!

31

Aviso: estoy dispuesto a dar una recompensa por cualquier respuesta que me parezca interesante.

Su desafío es diseñar una computadora de conjunto de instrucciones de Turing-complete (OISC):

Un OISC es una máquina abstracta que usa solo una instrucción, lo que evita la necesidad de un código de operación de lenguaje de máquina. Con una elección juiciosa para la instrucción individual y recursos infinitos dados, un OISC es capaz de ser una computadora universal de la misma manera que las computadoras tradicionales que tienen múltiples instrucciones.

Aquí hay algunos ejemplos de comandos únicos que hacen un OISC completo de Turing.

Reglas:

Debe proporcionar una interpretación o prueba de ello.

Debe proporcionar un intérprete para su idioma. Este intérprete solo debe estar restringido por memoria / tiempo (por ejemplo, no debe tener restricciones impuestas por el usuario). Si no proporciona un intérprete para su idioma (por cualquier otro motivo que no sea la pereza), debe demostrar que es posible que se escriba uno. Un intérprete debe ser posible .

Debes demostrar su integridad de Turing

Debe incluir una prueba formal de que su idioma es Turing completo. Una forma sencilla de hacerlo es demostrando que puede interpretar o tener el mismo comportamiento que otro lenguaje completo de Turing. El lenguaje más básico para interpretar sería Brainf ** k .

Por ejemplo, un lenguaje normal que tiene los mismos comandos que Brainf ** k (y la misma falta de restricciones de memoria impuestas por el usuario) es Turing completo porque todo lo que se puede implementar en Brainf ** k se puede implementar en el idioma .

Aquí hay una lista de lenguajes completos de Turing muy fáciles de implementar.

Requisitos adicionales de OISC

  • Este OISC solo debe tener una instrucción: no puede tener varias instrucciones con una de ellas, lo que lo hace completo para Turing.

  • Su OISC puede usar cualquier sintaxis que desee. Debe definir en su respuesta qué es la instrucción, qué son los datos y qué es un no operativo (por ejemplo, espacios en blanco). ¡Ser creativo!

  • Los argumentos no solo necesitan ser enteros. Por ejemplo, /// es un hermoso ejemplo de un OISC completo de Turing.

  • Cómo y si la entrada y la salida son tomadas y dadas son dejadas a usted. La mayoría de los OISC implementan E / S a través de ubicaciones de memoria específicas, pero puede haber otras formas de hacerlo, y le recomendamos que encuentre una.

  • Una respuesta válida debe proporcionar un código de ejemplo en su OISC, ya sea incluyéndolo en la publicación o vinculándolo a un desafío simple resuelto en el idioma.

Votación

Votantes, por favor recuerden no votar las presentaciones aburridas. Ejemplos:

  • Lenguage -equivalents
  • Una implementación de un OISC existente (respondedores, ¡cree el suyo propio!)
  • Un "OISC" en el que el primer argumento especifica un comando para llamar ( ejemplo )

Sin embargo, debe votar las presentaciones interesantes y creativas, como:

  • Un OISC basado en una ecuación matemática
  • Un ZISC completo de Turing basado en una red neuronal
  • Un OISC en el que la E / S de salida ocurre de formas distintas a ciertas ubicaciones de memoria

Victorioso

Al igual que con , ¡la respuesta con más votos gana! ¡Buena suerte!

MD XF
fuente
10
¿Qué es una "instrucción"? ¿Y cómo los contamos?
Wheat Wizard
1
@NoOneIsHere Ojalá supiera lo suficiente para votar xD
Brian H.
2
Voté a favor esto. Creo que esta es una idea muy interesante, pero no explica exactamente qué es un OISC y cómo confirmar que algo es uno. Hice BF un OISC, pero eso está claramente en contra del espíritu de la pregunta, pero técnicamente válido.
NoOneIsHere
1
@MDXF no creo que consigas ///: tiene un comando de sustitución y tiene comandos de impresión, que no es solo un efecto secundario del comando de sustitución
Destructible Lemon
1
@NoOneIsHere Porque concurso de popularidad . Sí, es válido, pero tiene una puntuación baja (cuenta de votos), por lo que no ganará.
user202729

Respuestas:

20

XOISC

Este OISC se basa en el combinador X de Fokker que se define de la siguiente manera:

X=λf .f (λg h x .g x (h x)) (λa b c .a)

Si reconocemos el hecho de que el cálculo de SKI está completando Turing, el combinador anterior también está completo. Esto se debe a que S , K y yo podemos escribir en términos de X , así:XSKIX

S=X (X X)K=X XI=S K K=X (X X) (X X) (X X)

Cómo funciona XOISC

Internamente, XOISC tiene una pila (inicialmente vacía), desde allí la instrucción que toma como argumento hace lo siguiente:n

  • Pop elementos (funciones f 1 ... f N ) de la pila, presione f 1 ( f 2 ( ... ( f N X ) ... ) )nf1fNf1 (f2 ((fN X)))

Una vez que no quedan más instrucciones, XOISC empujará todos los argumentos de la línea de comandos (si los hay) a la pila, por ejemplo:

[s1,, sMstack before, a1,, aNarguments]

El cálculo final será .((((s1 s2)) sM) a1))aN


Como la única instrucción en XOISC toma solo un argumento (desplazamiento de memoria), no hay razón para usar un nombre para esa instrucción. Por lo tanto, un archivo fuente válido constituirá únicamente enteros separados por líneas nuevas o espacios en blanco, como por ejemplo:

0 0 2 0 1 0 1

Pruébalo en línea!

Ejemplo

Tomemos el ejemplo anterior (la pila crece a la derecha):

0pop 0 and apply (ie. push single X):[X]0again simply push X:[X, X]2pop 2 (a,b) and push a (b X):[X (X X)]0simply push X:[X (X X), X]1pop 1 (a) and push a X:[X (X X), X X]0simply push X:[X (X X), X X, X]1pop 1 (a) and push a X:[X (X X), X X, X X]

Finalmente, evalúe la pila: o con menos paréntesis X ( X X ) ( X X ) ( X X ) que reconocemos como la buena identidad de S K K función.((X (X X)) (X X)) (X X)X (X X) (X X) (X X)S K K

Integridad de Turing

Idea de prueba

X

X

((X (X X)) (X X)) (X X)

  • X0
  • luego estamos en un nuevo nivel de paréntesis, por lo que nuevamente solo necesitamos un 0
  • ahora se cierran dos paréntesis, por lo que debemos mostrar 2 elementos: 2
  • de nuevo estamos en un nuevo nivel de paréntesis, por lo que necesitamos un 0
  • dos paréntesis, cierre así que de nuevo un 2
  • y lo mismo otra vez

Así que terminamos con un programa XOISC diferente (pero semánticamente equivalente):

0 0 2 0 2 0 2 Pruébalo en línea!

X

Prueba formal

Dado que el cálculo de SKI es Turing completo, debemos mostrar dos cosas:

  1. X
  2. X

La primera parte, que demuestra las tres igualdades en la introducción, es muy tediosa y requiere mucho espacio, y tampoco es muy interesante. Entonces, en lugar de ponerlo en esta publicación, puedes encontrarlo aquí * .

X

XXf gfg

0XF1FNG1GKfgf g

F1FN G1GK1 (GK+1)fggff g

Interprete

Entradas

Dado que el cálculo lambda sin tipo requiere que definamos nuestros propios tipos de datos para todo lo que queremos y esto es engorroso, el intérprete conoce los números de la Iglesia ; esto significa que cuando proporciona entradas, transformará automáticamente los números a su correspondiente número de Iglesia.

Como ejemplo, aquí hay un programa que multiplica dos números: ¡ Pruébelo en línea!

También puede proporcionar funciones como argumentos mediante el uso de índices De Bruijn , por ejemplo, el Scombinador \\\(3 1 (2 1))(o λλλ(3 1 (2 1))). Sin embargo, también reconoce el S, K, Iy por supuesto Xcombinador.

Salida

Por defecto, el intérprete verifica si la salida codifica un número entero; si lo hace, generará el número correspondiente (además del resultado). Por conveniencia, está la -bbandera que le dice al intérprete que intente hacer coincidir un booleano (vea el último ejemplo).

Ensamblador

Por supuesto, cualquier lenguaje de bajo nivel necesita un ensamblador que convierta un lenguaje de alto nivel en él, simplemente puede usar cualquier entrada (ver arriba) y traducirlo a un programa XOISC usando la -abandera, ¡ pruébelo en línea! ** **


* En caso de que el enlace esté inactivo, hay una copia como comentario HTML en esta publicación.

** Esto da como resultado un programa que prueba la originalidad, ¡ pruébelo en línea!

ბიმო
fuente
1
¿Hay alguna razón por la que elegiste el combinador X en lugar del combinador Iota?
Esolanging Fruit
1
@EsolangingFruit: Sí, también hay varias otras opciones, al final opté por esa porque usa la menor cantidad de aplicaciones para construir SK. Parecía que funcionaría mejor (tbh, no he hecho una comparación).
ბიმო
1
Por cierto. Si está interesado, hay una buena comparación de varios combinadores en el documento vinculado.
ბიმო
19

Dibujar

Draw es un OISC que actúa en una cuadrícula 2D, marcando cuadrados de manera similar a la máquina Wang B. Sin embargo, para mantener el lenguaje lo más simple y OISC-y posible, todas las instrucciones (de las cuales hay un gran total de uno) marcan el cuadrado que acaba de pisar y, para poder detenerse, pisar un cuadrado marcado termina el programa

El programa consiste en una secuencia de líneas que contienen un identificador de línea (cadena arbitraria que no incluye # o espacios en blanco), dos enteros ( xy y) y dos identificadores de línea más ( ay b).

El programa se ejecuta de la siguiente manera:
A partir de la línea identificada como startcon el puntero que apunta a la posición (0, 0), mover el puntero por la cantidad dada por xy yy marcan la plaza el puntero se encuentra ahora en adelante (a menos que la plaza ya está marcado, en cuyo caso la ejecución termina). Luego, salta a la línea asi al menos uno de los cuadrados directamente adyacentes también está marcado, y a la línea de lo bcontrario.

Se alienta a los intérpretes a mostrar el resultado final de la cuadrícula como algún tipo de imagen, lienzo, etc.

Integridad de Turing

Draw es Turing completo, ya que es posible compilar una versión modificada (llamada alternativa) de una máquina Minsky en el idioma.

Alternate actúa de manera similar a una máquina Minsky de dos contadores, pero existe una gran restricción en los comandos: los comandos deben alternar entre apuntar al primer y segundo contador. Para conseguir alrededor de esta modificación, un comando adicional se ha añadido: nop. Este comando no cambia el contador objetivo en absoluto, lo que hace posible "rellenar" los cambios consecutivos a un contador, satisfaciendo la restricción descrita anteriormente. Esto también significa que el registro que se va a modificar no tiene que ser dado y, para cualquier instrucción dada, se puede inferir directamente de las instrucciones desde las cuales la ejecución puede saltar a él.

Ejemplo: esta máquina Minsky

1 inc A 2
2 inc A 3
3 dec A 3 4
4 halt

se convierte en este programa alternativo:

1 inc 2
2 nop 3
3 inc 4
4 nop 5
5 dec 6 8
6 nop 5
7 halt
8 halt

Esta restricción es necesaria debido a la forma en que el programa Draw eventual maneja los registros, lo que quiere decir que no los diferencia en absoluto. En cambio, el programa Draw simplemente copia el registro que no ha sido modificado por la instrucción anterior, modificándolo de acuerdo con la instrucción que se está ejecutando.

Luego, el programa alternativo se traduce directamente en Draw de la siguiente manera:

El programa comienza con este bloque.

start 0 0 a a
a 3 0 b b
b -3 1 c c
c 3 0 d d
d -3 2 e e
e 3 0 f f
f 3 -3 i1_a i1_a

inc, decy nopse traducen casi de la misma manera entre sí. En todos los casos, no hay diferencia entre cambiar el primer o el segundo registro (como se explicó anteriormente). Aquí hay un incremento, equivalente a inc 2:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 0 i2_z i2_y
i1_f 5 0 i2_z i2_y

Cambie los números en las i1_xpartes al índice de la instrucción actual, y en las i2_xpartes al índice de la siguiente instrucción a ejecutar.

La nopinstrucción se puede traducir como tal:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 -2 i2_z i2_y
i1_f 5 -2 i2_z i2_y

Esta es una disminución:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 -2 i3_z i3_y
i1_f 5 -4 i2_z i2_y

i3_x se refiere a la instrucción a llamar si el contador ya es 1.

Detener:

i1_y 0 0 0 0
i1_z 0 0 0 0

Cambie las etiquetas adecuadamente y simplemente encadene todo. Hacer esto para el ejemplo de arriba da el programa Draw en el repositorio de arriba.

Intérpretes

Actualmente hay dos intérpretes, ambos escritos en Python. Se pueden encontrar en el repositorio GitHub de Draw .

  1. draw.py : este intérprete está destinado a la línea de comandos y toma el origen del programa como argumento. Después de cada paso, genera el comando que se ejecutó y la ubicación del puntero de instrucción; después de que el programa se detiene, imprime el número de celdas marcadas.
  2. draw_golly.py : esta versión usa Golly para el resultado gráfico más fácil y con el propósito equivocado , tomando la fuente a través de un cuadro emergente al iniciar el script. Golly puede ser un poco quisquilloso con Python, así que asegúrese de tener instalado Python 2 (y no mezcle Golly de 32 bits con Python de 64 bits o viceversa). La salida se proporciona a través de la cuadrícula de celdas incorporada de Golly.

La siguiente imagen es un ejemplo para la salida del segundo intérprete. Ejecutar el programa de ejemplo en el repositorio da esto (o similar):

ivzem
fuente
1
¡Asombroso! Felicidades por encontrar una forma única de hacer el desafío.
MD XF
Su idioma no necesita detenerse para estar completo. La regla 110 no termina, pero de todos modos está completa.
Akangka
+1 para Golly, el mejor simulador de autómatas celulares.
HighlyRadioactive
14

-3

Aquí está la esencia.

Memoria

La memoria es un mapa de cintas, donde las claves son cadenas y los valores son enteros de tamaño arbitrario.

Además, hay un conjunto de etiquetas, donde el programa puede saltar.

Hay una pila, que contiene los operandos, que son cadenas.

Hay una ofensa, que controla dónde puede acceder en las cintas de la memoria.

La única instrucción

-. Primero, saca una cadena LABELde la pila. Si eso LABELno está definido como una etiqueta, define la etiqueta y borra la fuente de esa etiqueta (es decir, de dónde fue empujada) y la instrucción actual. De lo contrario, realiza el siguiente cálculo, utilizando los dos valores superiores Ay B:

if mem[A] < mem[B]:
    jump to LABEL
if mem[A] != mem[B]:
    mem[A]--
else:
    mem[B]++

Tenga en cuenta que si hay argumentos en exceso o argumentos insuficientes, el programa generará un error, mostrando el estado del programa.

El desplazamiento se puede modificar accediendo al valor de ..

Código de ejemplo

X-

i i X-
i i X-
i i X-
i i X-
i i X-
i i X-
i i X-

Esto establece la variable ien 7, incrementando los 7tiempos.

X-

i i X-
i i X-
i i X-
LOOP-
    a a X-
    a a X-
    j i LOOP-

Esto se multiplica i+1por la constante 2.

Prueba de integridad de Turing

Sin tener en cuenta los tamaños int de C ++ (es decir, suponiendo que sean infinitos), -3 es Turing Completo por reducción a un brainfuck de 3 células . Puedo ignorar este tamaño porque se puede escribir un intérprete para -3 en una computadora con memoria infinita que tiene celdas arbitrariamente grandes.

También creo que cualquier BCT puede escribirse como un programa -3.

Conor O'Brien
fuente
Como me encanta mejorar mi contenido, agradecería una explicación sobre el voto negativo
Conor O'Brien