Sé que, en los lenguajes de programación imperativos, un bucle while-do es suficiente como una construcción de flujo de control para completar el lenguaje Turing (en lo que respecta al flujo de control, por supuesto, también necesitamos memoria ilimitada y ciertos operadores ...) . La esencia de mi pregunta es: ¿un ciclo do-while tiene el mismo poder computacional que un ciclo while-do? En otras palabras, ¿puede un idioma ser Turing completo si es imposible omitir las instrucciones por completo?
Me doy cuenta de que algunas de las semánticas aquí podrían ser un poco ambiguas, así que permítanme formular la pregunta real con un ejemplo específico:
Brainfuck (BF) es un tarpit de Turing donde el único flujo de control es un ciclo while-do, denotado como [...]
(hay una especificación de lenguaje completa en la parte inferior de la pregunta, en caso de que no esté familiarizado con Brainfuck). Definamos un nuevo lenguaje BF *, donde ,.+-<>
tenga la misma semántica que en BF, pero en lugar de la []
que tenemos, {}
denota un ciclo do-while. Es decir, la única diferencia con BF es que cada bucle se ejecuta al menos una vez antes de que se puedan omitir más iteraciones.
¿Está BF * Turing completo? Si es así, me interesaría cómo podría traducir BF a BF *. Si no es así, ¿cómo pruebo eso?
Algunas observaciones mías:
- No todos los programas BF se pueden traducir a BF *. Por ejemplo, es imposible escribir un programa en BF * que pueda o no leer o imprimir un valor; si el programa potencialmente imprime uno o más valores, siempre imprimirá al menos uno. Sin embargo, puede haber un subconjunto de BF completo de Turing que se puede traducir a BF *.
- No podemos simplemente traducir
[f]
(dondef
hay algún programa arbitrario, Brainfuck que consiste solo en+-[]<>
) a (en un intento de cancelar el efecto de la primera iteración), porque a) no todas las funciones computables tienen un inverso computable yb) incluso si lo hiciera, no necesariamente tendría menos bucles que, por lo tanto, aplicar este paso de forma recursiva no garantiza que termine en primer lugar.f-1{f}
f-1
f
Aquí hay una descripción rápida sobre el lenguaje Brainfuck. Brainfuck opera en una cinta infinita donde cada celda contiene valores de bytes, inicialmente cero. Los desbordamientos se envuelven, por lo que al incrementar 255 se obtiene 0 y viceversa. El lenguaje consta de 8 instrucciones:
+ Increment the current cell.
- Decrement the current cell.
> Move tape head to the right.
< Move tape head to the left.
, Input a character from STDIN into the current cell.
. Output the current cell as a character to STDOUT.
[ If the current cell is zero, jump past the matching ].
] If the current cell is non-zero, jump back to just behind the matching [.
[]
no está definiendo exactamente un bucle "while do" en BF. Como en su tabla, los corchetes izquierdo y derecho evalúan la celda actual cero / no cero. Entonces, ¿cuál es la descripción exacta de la{}
lógica de evaluación de llaves correspondiente ? Sugerir más diálogo / discusión en Computer Science Chat . también sus "observaciones" son más como "postulados" o "proposiciones" sin prueba.{}
sería{
no hacer nada en absoluto y}
lo mismo]
. No tendré mucho tiempo en los próximos días, pero me reuniré con usted en el chat cuando encuentre algo de tiempo.{}
y quitando[]
, es BF * Turing completo. con el entendimiento de que BF[]
es una construcción solo algo similar / análogo a un ciclo while-do en los lenguajes completos de Turing.Respuestas:
No sé Brainfuck, así que tendrás que hacer alguna traducción de mi pseudocódigo. Pero, suponiendo que Brainfuck se comporte con sensatez (¡ja!), Todo lo que sigue debe aplicarse.
do-while es equivalente a while-do.
do X while Y
es equivalente aX; while Y do X
y, suponiendo que tenga condicionales,while Y do X
es equivalente aif Y then (do X while Y)
.La vida es un poco más difícil si no tienes condicionales. Si tiene while-do, puede simular
if Y then X
usando algo como esto:Pero, ¿qué pasa si solo tienes do-while? Afirmo que lo siguiente simula
if Y then X
, suponiendo queX
termina dado el valor actual de las variables. (Esto no está garantizado: el programaif y=0 then loopforever
termina siy != 0
, a pesar de que realiza unX
bucle para cualquier valor de las variables). SeanV1
, ...,Vn
las variables modificadas porX
yX'
seanX
modificadas para que se utilicen enVi'
lugar deVi
para cada una de esas variables.swap(A,B)
denota el código obvio que intercambia las variablesA
yB
.La idea es la siguiente. Primero, suponga que eso
Y
es falso. Simulamos hacerX
una vez, y almacenamos los resultados enV1''
, ...,Vn''
;V1'
, ...,Vn'
mantenga los valores originales deV1
, ...,Vn
. Luego, asignamosV1 := V1'; ...; Vn := Vn'
, que no hace nada. Entonces, siY
es falso, no hemos hecho nada. Ahora, supongamos que esoY
es cierto. Ahora simularemosX
dos veces , almacenando los resultados en las variables "cebado" y "cebado doble". Entonces, ahora, las asignaciones al final del ciclo tienen el efecto queX
se calculó una vez. Tenga en cuenta queY
solo depende de las variables "sin imprimir", por lo que su valor no se ve afectado al ejecutar el bucle repetidamente.Bien, ¿y qué tal si
X
no termina el valor actual de las variables? (Gracias a Martin Ender por señalar esta posibilidad). En ese caso, necesitamos simularX
instrucción por instrucción, usando ideas similares a las anteriores. Cada instrucción definitivamente termina para que podamos usar laif
simulación anterior para hacer la decodificación de instrucciones, en la línea de "Si el código de operación es foo, haz esto; si es bar, haz eso; ...". Entonces, ahora, usamos un bucle para iterar a través de las instrucciones deX
, usando un puntero de instrucción y así sucesivamente para que sepamos qué instrucción ejecutar a continuación. Al final de cada iteración del ciclo, verifiqueY
y verifique si seX
ha detenido todavía. SiY
es falso, la técnica de intercambio nos permite deshacer los efectos deX
La primera instrucción.fuente
Y
es falso peroX
no termina en el conjunto actual de valores variables.if Y then X
termina, pero su traducción no, porque siempre debe ejecutarseX'
al menos una vez.X
instrucción por instrucción y verificarY
después de cada instrucción. Se garantiza que cada instrucción terminará para que todo funcione. Pero es un dolor escribirlo.X
así si comienza con un ciclo while / condicional. Tendré que pensar en esto un poco más.X'
no es lineal. ¿Te importaría incluir más detalles para un juguete simple pero no trivialX
? Por ejemplodo (while A do B) while C
? (lo externodo while
viene de lo externowhile do
que estamos traduciendo actualmente)