Programowałem ostatnio w brainfucku i natrafiłem na problem: jak zrobić normalnego ifa? Niby proste, zrobić przy użyciu poleceń [ i ] pętlę, która wykona się najwyżej raz. No ale skoro to ma być normalny if w środku programu, to w praktyce zwykle chciałbym, żeby po zakończeniu tego ifa wskaźnik pamięci (wiecie, ten przesuwany poleceniami < i >) był w tym samym miejscu niezależnie od tego, czy wchodziliśmy w ifa czy też go ominęliśmy. No i oto moje przemyślenia na temat tego, jak to zrobić.
Przede wszystkim jeśli zrobię ifa tak jak mi po prostu przyjdzie do głowy, to nie będzie tak jak chcę. No bo zobacz, oto na przykład negacja - kawałek kodu, który:
>+<[>-]
Możesz sobie przetestować na stronie internetowej do wizualizacji Brainfucka. Jeśli puścisz ten kod tak, jak pokazuję, to zobaczysz, co się dzieje, kiedy w pierwszej komórce jest 0. Jeśli chcesz zobaczyć, co się dzieje, kiedy w pierwszej komórce jest 1, na początku programu dopisz jeszcze +.
No i jak widzisz negacja działa, ale w zależności od tego, czy wchodzimy w tego ifa czy nie (czyli czy polecenie [ wykonuje skok o kilka poleceń w prawo czy nie), po ifie lądujemy albo w komórce pierwszej, albo w drugiej.
Kiedy T. walczył z tym problemem, to wymyślił takie obejście, że dobrze, będziemy po ifie lądować w różnych komórkach pamięci, ale potem miejmy specjalny kod ustawiający wskaźnik pamięci w stałym miejscu. Na przykład miejmy gdzieś z boku ciąg jedynek poprzedzony zerem, przesuwajmy wskaźnik pamięci w jego środek (skoro tych jedynek wiele, to nie trzeba celować dokładnie) a potem dajmy pętlę jadącą w lewo do zera. Coś takiego na przykład:
>>>>+>+>+>+<<<<<<< >+<[>-] >>>>> [<]
Fajne, działa. Ale nie podoba mi się, że to takie rozrzutne - i w pamięci muszę mieć ten obszar z jedynkami (dobrze, mógłby być krótszy, no ale jednak), i w kodzie mam ten blok dodatkowy po ifie.
No więc kombinowałem, jak to zrobić. Patrzcie: jeśli po poleceniu ] chcę mieć wskaźnik pamięci w tym samym miejscu niezależnie od tego, czy [ przeskakiwało ciało ifa czy nie, to po poleceniu ] wskaźnik pamięci musi być na tej samej komórce, na której jest przy poleceniu [. No bo przecież jeśli polecenie [ zrobi skok, to od razu po poleceniu [ będzie polecenie ], więc wskaźnik pamięci będzie tam samo. No a z drugiej strony przy poleceniu ] jeśli chcemy już wyjść z ciała ifa (czyli nie chcemy robić kolejnego obrotu pętli), to wskaźnik pamięci musi wskazywać na komórkę, w której jest zero (bo tak działa polecenie ]).
No a przecież na poleceniu [ wskaźnik pamięci musi wskazywać na komórkę, w której jest czasem 0, a czasem 1 - bo to jest istota ifa. Wniosek: żeby if działał tak jak chcę (kończył działanie ze wskaźnikiem pamięci na tej samej komórce niezależnie czy warunek ifa był spełniony czy nie, to jest czy wchodziliśmy w ciało ifa czy nie), to if musi niszczyć komórkę ze swoim warunkiem. Znaczy, po wykonaniu ifa komórka z warunkiem musi zostać wyzerowana. Inaczej się nie da. Po prostu, if musi na koniec ciała przestawić wskaźnik pamięci tam, gdzie był na poleceniu [ i wyzerować tę komórkę pamięci, żeby polecenie ] nie zapętliło.
Patrzcie, tak to można zrobić - oto negacja, podobna do poprzedniej, ale która zawsze ląduje na koniec wskaźnikiem pamięci na komórce pierwszej (tej negowanej), ale za to ją zeruje:
>+< [>-<-]
Ale czy to jest lepsze od sposobu T. to nie wiem, to zależy. Kod trochę krótszy, pamięci nie zużywa dodatkowej, ale za to niszczy negowaną komórkę. Więc jeśli chcę jej nie niszczyć, to muszę ją najpierw skopiować na bok i tam wykonać negację, a to za darmo też nie jest.