Wiem, że w imperatywnych językach programowania pętla while-do jest wystarczająca jako konstrukcja przepływu sterowania, aby uzupełnić język Turinga (jeśli chodzi o przepływ sterowania - oczywiście potrzebujemy również nieograniczonej pamięci i niektórych operatorów ...) . Istota mojego pytania brzmi: czy pętla „do-while” ma taką samą moc obliczeniową jak pętla „do-do”? Innymi słowy, czy język może być kompletny przez Turinga, jeśli nie można całkowicie pominąć instrukcji.
Zdaję sobie sprawę, że niektóre semantyki mogą być nieco niejednoznaczne, więc pozwólcie, że sformułuję rzeczywiste pytanie konkretnym przykładem:
Brainfuck (BF) to tarpit Turinga, w którym jedynym przepływem kontrolnym jest pętla while-do, oznaczona jako [...]
(na dole pytania znajduje się pełna specyfikacja języka, na wypadek, gdyby nie znasz Brainfuck). Zdefiniujmy nowy język BF *, w którym ,.+-<>
ma taką samą semantykę jak w BF, ale zamiast tego []
mamy {}
co oznacza pętlę „do-while”. Oznacza to, że jedyną różnicą w stosunku do BF jest to, że każda pętla jest wykonywana co najmniej raz, zanim możliwe będzie pominięcie kolejnych iteracji.
Czy BF * Turing jest kompletny? Jeśli tak, byłbym zainteresowany tym, jak mógłbym przetłumaczyć BF na BF *. Jeśli nie, to jak to udowodnić?
Kilka moich własnych obserwacji:
- Nie każdy program BF można przetłumaczyć na BF *. Na przykład niemożliwe jest napisanie programu w BF *, który może, ale nie musi, odczytać lub wydrukować wartość - jeśli program potencjalnie wydrukuje jedną lub więcej wartości, zawsze wydrukuje przynajmniej jedną. Jednak może istnieć kompletny podzbiór BF Turinga, który można przetłumaczyć na BF *.
- Nie możemy po prostu tłumaczyć
[f]
(gdzief
jest jakiś arbitralny program składający się tylko z Brainfuck+-[]<>
) na (w celu anulowania efektu pierwszej iteracji), ponieważ a) nie każda funkcja obliczalna ma obliczalną odwrotność i b) nawet jeśli tak, niekoniecznie miałoby mniej pętli niż dlatego rekursywne wykonanie tego kroku nie gwarantuje zakończenia.f-1{f}
f-1
f
Oto krótki przegląd języka Brainfuck. Brainfuck działa na nieskończonej taśmie, w której każda komórka zawiera wartości bajtów, początkowo zerowe. Przepełnienia się zawijają, więc zwiększenie 255 daje 0 i odwrotnie. Język składa się z 8 instrukcji:
+ 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 [.
[]
nie definiuje dokładnie pętli „while do” w BF. tak jak w tabeli, lewy i prawy nawias oceniają bieżącą komórkę zero / zero. więc jaki jest dokładny opis odpowiedniej{}
logiki oceny nawiasów klamrowych? zaproponuj dalsze dialogi / dyskusje na czacie informatyki . również „obserwacje” bardziej przypominają „postulaty” lub „twierdzenia” bez dowodu.{}
byłoby{
nic nie robić i}
to samo co]
. W ciągu najbliższych kilku dni nie będę miał dużo czasu, ale dołączę do ciebie na czacie, kiedy znajdę trochę czasu.{}
i odebraniem[]
, BF * Turing jest kompletny. ze zrozumieniem, że BF[]
jest konstrukcją tylko czymś podobnym / analogicznym do pętli while-do w kompletnych językach Turinga.Odpowiedzi:
Nie znam Brainfuck, więc będziesz musiał zrobić tłumaczenie z mojego pseudokodu. Ale zakładając, że Brainfuck zachowuje się rozsądnie (ha!), Wszystko poniżej powinno mieć zastosowanie.
do-while jest równoważne do while-do.
do X while Y
jest równoważneX; while Y do X
i, zakładając, że masz warunki warunkowe,while Y do X
jest równoważne zif Y then (do X while Y)
.Życie jest trochę trudniejsze, jeśli nie masz warunkowych. Jeśli masz czas na wykonanie, możesz przeprowadzić symulację,
if Y then X
używając czegoś takiego:Ale co, jeśli masz tylko czas do zrobienia? Twierdzę, że następujące symuluje
if Y then X
, zakładając, żeX
kończy się, biorąc pod uwagę bieżącą wartość zmiennych. (Nie jest to gwarantowane: programif y=0 then loopforever
kończy działaniey != 0
, nawet jeśliX
zapętla dowolną wartość zmiennych). NiechV1
...Vn
będą zmiennymi modyfikowanymi przezX
i niechX'
będąX
modyfikowane, aby używałVi'
zamiastVi
dla każdej z tych zmiennych.swap(A,B)
oznacza oczywisty kod, który zamienia zmienneA
iB
.Pomysł jest następujący. Po pierwsze, załóżmy, że
Y
to fałsz. Symulujemy zrobienie tegoX
raz i przechowujemy wyniki wV1''
...,Vn''
;V1'
, ...Vn'
zachowaj oryginalne wartościV1
, ...,Vn
. Następnie przypisujemyV1 := V1'; ...; Vn := Vn'
, co nie robi nic. Więc jeśliY
jest to fałsz, nic nie zrobiliśmy. Załóżmy, żeY
to prawda. Teraz przeprowadzimy symulacjęX
dwa razy , przechowując wyniki zarówno w zmiennych „zagruntowanych”, jak i „podwójnie zagruntowanych”. Zatem teraz przypisania na końcu pętli mają efekt, któryX
został obliczony raz. Zauważ, żeY
zależy tylko od zmiennych „nieprzygotowanych”, więc na jego wartość nie ma wpływu wielokrotne uruchamianie pętli.OK, a co jeśli
X
nie może zakończyć się dla bieżącej wartości zmiennych? (Podziękowania dla Martina Endera za wskazanie tej możliwości.) W takim przypadku musimy przeprowadzić symulacjęX
instrukcja po instrukcji, używając podobnych pomysłów do powyższych. Każda instrukcja definitywnie kończy się, więc możemy użyćif
powyższej symulacji, aby wykonać dekodowanie instrukcji, zgodnie z zasadą „Jeśli kod operacji jest foo, zrób to; jeśli to pasek, zrób to; ...”. Tak więc teraz używamy pętli, aby iterować instrukcjeX
, używając wskaźnika instrukcji i tak dalej, abyśmy wiedzieli, którą instrukcję wykonać następnie. Na końcu każdej iteracji pętli sprawdźY
i sprawdź, czyX
jeszcze się zatrzymała. JeśliY
jest fałszem, technika zamiany pozwala nam cofnąć efektyX
pierwsza instrukcja.źródło
Y
jest fałsz, aleX
nie kończy się na bieżącym zestawie wartości zmiennych.if Y then X
kończy się, ale tłumaczenie nie, ponieważ zawsze musi zostać wykonaneX'
co najmniej raz.X
instrukcja po instrukcji i sprawdzićY
po każdej instrukcji. Każda instrukcja ma gwarancję zakończenia, więc wszystko będzie działać. Ale zapisywanie jest uciążliwe.X
tak dekonstruować , jeśli zaczyna się od pętli while / warunkowej. Muszę się nad tym zastanowić.X'
jest nieliniowa. Czy mógłbyś podać więcej szczegółów na temat prostej, ale nietrywialnej zabawkiX
? Na przykładdo (while A do B) while C
? (zewnętrznydo while
pochodzi z zewnętrznegowhile do
, który obecnie tłumaczymy)