Un ciclo do-while è sufficiente per la completezza di Turing?


22

So che, nei linguaggi di programmazione imperativa, un ciclo while-do è sufficiente come costrutto del flusso di controllo per rendere completo il linguaggio Turing (per quanto riguarda il flusso di controllo - ovviamente abbiamo anche bisogno di memoria illimitata e alcuni operatori ...) . L'essenza della mia domanda è: un ciclo do-while ha la stessa potenza computazionale di un ciclo while-do? In altre parole, una lingua può essere Turing completa se è impossibile saltare del tutto le istruzioni.

Mi rendo conto che alcune delle semantiche qui potrebbero essere un po 'ambigue, quindi lasciatemi esprimere la domanda effettiva con un esempio specifico:

Brainfuck (BF) è un tarpit di Turing in cui l'unico flusso di controllo è un loop while-do, indicato come [...](c'è una specifica di linguaggio completa nella parte inferiore della domanda, nel caso in cui non si abbia familiarità con Brainfuck). Definiamo un nuovo linguaggio BF *, dove ,.+-<>ha la stessa semantica di BF, ma invece di []quello {}che denota un ciclo do-while. Cioè, l'unica differenza rispetto a BF è che ogni ciclo viene eseguito almeno una volta prima di poter saltare ulteriori iterazioni.

BF * Turing è completo? In tal caso, sarei interessato a come avrei potuto tradurre da BF a BF *. In caso contrario, come posso dimostrarlo?

Alcune mie osservazioni:

  • Non tutti i programmi BF possono essere tradotti in BF *. Ad esempio, è impossibile scrivere un programma in BF * che potrebbe o meno leggere o stampare un valore - se il programma potenzialmente stampa uno o più valori, ne stamperà sempre almeno uno. Tuttavia, potrebbe esserci un sottoinsieme completo di Turing di BF che può essere tradotto in BF *.
  • Non possiamo semplicemente tradurre [f](dove si ftrova un programma Brainfuck arbitrario costituito solo da +-[]<>) in (nel tentativo di annullare l'effetto della prima iterazione), perché a) non tutte le funzioni calcolabili hanno un inverso calcolabile eb) anche se lo facesse, non avrebbe necessariamente un numero inferiore di loop rispetto all'applicazione, quindi applicare questo passaggio in modo ricorsivo non è garantito per terminare in primo luogo.f-1{f}f-1f

Ecco una rapida panoramica del linguaggio Brainfuck. Brainfuck opera su un nastro infinito in cui ogni cella contiene un valore di byte, inizialmente zero. Gli overflow si avvolgono, quindi l'incremento di 255 dà 0 e viceversa. La lingua è composta da 8 istruzioni:

+   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 [.


interessante ma penso che non sia stato costruito con cura. []non sta esattamente definendo un ciclo "while do" in BF. come nella tabella le parentesi sinistra e destra valutano la cella zero / diversa da zero. quindi qual è la descrizione esatta della corrispondente {}logica di valutazione delle parentesi graffe? suggerire ulteriori dialoghi / discussioni in Computer Science Chat . anche le tue "osservazioni" sono più simili a "postulati" o "proposizioni" senza prove.
vzn,

@vzn Questi sono buoni punti. Ho pensato che l'ovvia definizione di {}sarebbe stata quella di {non fare assolutamente nulla e }lo stesso di ]. Non avrò molto tempo nei prossimi giorni, ma mi unirò a te in chat quando troverò del tempo.
Martin Ender,

quindi purtroppo questo è apparentemente un po 'sottile da porre e sembrano esserci due domande totalmente diverse qui. (1) dato qualsiasi linguaggio completo Turing con un ciclo while-do (e "altre cose"), può invece essere convertito in un linguaggio completo Turing con solo un ciclo do-while. ma poi bisogna sapere di più sulle "altre cose" in dettaglio per rispondere. (2) dato BF e un nuovo BF * con data definizione {}e rimozione [], BF * Turing è completo. con la consapevolezza che BF []è un costrutto solo qualcosa di simile / analogo a un ciclo while-do nei linguaggi completi di Turing.
vzn,

1
La parte @vzn (1) era solo la parte TL; DR della mia domanda. Sono pienamente consapevole che è probabilmente impossibile rispondere per "un po 'di lingua". Questo è il motivo per cui ho formulato la vera domanda in termini di un linguaggio giocattolo molto semplice (BF) per restringerlo davvero al comportamento dei loop (perché ho pensato che BF * potesse essere mostrato come TC che lo avrebbe reso più semplice per mostrarlo per altre lingue che hanno solo loop do-while). Non sono sicuro di come pensi che i loop BF siano diversi dai loop while-do di altre lingue.
Martin Ender,

Risposte:


10

Non conosco Brainfuck, quindi dovrai fare una traduzione dal mio pseudocodice. Ma supponendo che Brainfuck si comporti in modo sensato (ah!), Tutto ciò che segue dovrebbe applicarsi.

do-while equivale a while-do. do X while Yè equivalente a X; while Y do Xe, supponendo che abbiate dei condizionali, while Y do Xè equivalente a if Y then (do X while Y).

La vita è un po 'più dura se non hai i condizionali. Se hai tempo di attesa, puoi simulare if Y then Xusando qualcosa del genere:

B := true
while (Y and B) do
    X
    B := false
endwhile

Ma cosa succede se hai solo da fare? Dichiaro che ciò che segue simula if Y then X, supponendo che Xtermina dato il valore corrente delle variabili. (Questo non è garantito: il programma if y=0 then loopforevertermina se y != 0, anche se Xesegue il loop per qualsiasi valore delle variabili). Sia V1, ..., Vnessere le variabili modificate Xe lasciate X'essere Xmodificate in modo che utilizzi Vi'anziché Viper ciascuna di quelle variabili. swap(A,B)denota il codice ovvio che scambia le variabili Ae B.

V1' := V1; ...; Vn' := Vn
V1'' := V1; ...; Vn'' := Vn
C := 0
do
    X'
    swap (V1',V1''); ...; swap (Vn',Vn'')
    C := C+1
while Y and C<2
V1 := V1'; ...; Vn := Vn'

L'idea è la seguente. In primo luogo, supponiamo che Ysia falso. Simuliamo il fare Xuna volta e archiviamo i risultati in V1'', ..., Vn''; V1', ..., Vn'conserva i valori originali di V1, ..., Vn. Quindi, assegniamo V1 := V1'; ...; Vn := Vn', che non fa nulla. Quindi, se Yè falso, non abbiamo fatto nulla. Supponiamo che Ysia vero. Ora simuleremo X due volte , memorizzando i risultati in entrambe le variabili "innescate" e "ad innescate doppie". Quindi, ora, le assegnazioni alla fine del loop hanno l'effetto che è Xstato calcolato una volta. Si noti che Ydipende solo dalle variabili "senza primer", quindi il suo valore non è influenzato dall'esecuzione ripetuta del ciclo.

OK, e se Xnon fosse possibile terminare per il valore corrente delle variabili? (Grazie a Martin Ender per aver sottolineato questa possibilità.) In tal caso, dobbiamo simulare Xistruzioni per istruzioni, usando idee simili a quelle sopra. Ogni istruzione termina definitivamente in modo che possiamo usare la ifsimulazione sopra per eseguire la decodifica delle istruzioni, sulla falsariga di "Se il codice operativo è foo, fallo; se è barra, fallo; ...". Quindi, ora, utilizziamo un ciclo per scorrere le istruzioni di X, usando un puntatore a istruzioni e così via in modo da sapere quale istruzione eseguire successivamente. Alla fine di ogni iterazione del ciclo, controlla Ye controlla se si Xè ancora fermato. Se Yè falso, la tecnica di scambio ci consente di annullare gli effetti diXprima istruzione.


1
Questa è un'idea chiara, ma penso che ci sia un problema qui: considera il caso in cui Yè falso ma Xnon termina sull'insieme corrente di valori variabili. if Y then Xtermina, ma la tua traduzione no, perché deve sempre essere eseguita X'almeno una volta.
Martin Ender,

1
@ MartinBüttner Urgh. Hai ragione. Quindi abbiamo bisogno di usare il loop per simulare Xistruzioni per istruzioni e controllare Ydopo ogni istruzione. Ogni istruzione è garantita per terminare, quindi tutto funzionerà. Ma è un dolore scrivere.
David Richerby,

1
Non sono del tutto sicuro se è possibile decostruire in Xquesto modo se inizia con un ciclo while / condizionale stesso. Dovrò pensarci ancora un po '.
Martin Ender,

Inoltre "Quindi, ora, utilizziamo un ciclo per scorrere le istruzioni di X, usando un puntatore a istruzioni e così via in modo da sapere quale istruzione eseguire successivamente". Sento che questo di per sé potrebbe richiedere una condizione di qualche tipo.
Martin Ender,

1
Non sono ancora del tutto sicuro su come definire "ogni istruzione" se X'non è lineare. Ti dispiacerebbe includere qualche dettaglio in più per un giocattolo semplice ma non banale X? Per esempio do (while A do B) while C? (l'esterno do whileproviene dall'esterno while doche stiamo attualmente traducendo)
Martin Ender,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.