1. Podstawy
Aby zrozumieć Brainfuck, musisz wyobrazić sobie nieskończoną liczbę komórek inicjowanych przez 0
każdą z nich.
...[0][0][0][0][0]...
Kiedy program „brainfuck” zaczyna wskazywać na dowolną komórkę.
...[0][0][*0*][0][0]...
Jeśli przesuniesz wskaźnik w prawo >
, przesuniesz wskaźnik z komórki X do komórki X + 1
...[0][0][0][*0*][0]...
Jeśli zwiększysz wartość komórki +
, otrzymasz:
...[0][0][0][*1*][0]...
Jeśli ponownie zwiększysz wartość komórki +
, otrzymasz:
...[0][0][0][*2*][0]...
Jeśli zmniejszysz wartość komórki -
, otrzymasz:
...[0][0][0][*1*][0]...
Jeśli przesuniesz wskaźnik w lewo <
, przesuniesz wskaźnik z komórki X do komórki X-1
...[0][0][*0*][1][0]...
2. Wejście
Aby przeczytać znak, użyj przecinka ,
. Co robi to: czyta znak ze standardowego wejścia i zapisuje jego dziesiętny kod ASCII do właściwej komórki.
Spójrz na tabelę ASCII . Na przykład kod dziesiętny !
to 33
, podczas gdy a
jest 97
.
Cóż, wyobraźmy sobie, że pamięć programu BF wygląda następująco:
...[0][0][*0*][0][0]...
Zakładając, że standardowe wejście oznacza a
, jeśli używasz ,
operatora przecinka , to, co robi BF, to odczyt a
dziesiętny kod ASCII 97
do pamięci:
...[0][0][*97*][0][0]...
Na ogół chcesz tak myśleć, ale prawda jest nieco bardziej złożona. Prawda jest taka, że BF nie czyta znaku, ale bajt (cokolwiek to jest). Pokażę Ci przykład:
W systemie linux
$ printf ł
wydruki:
ł
co jest specyficznym polskim charakterem. Ten znak nie jest kodowany za pomocą kodowania ASCII. W tym przypadku jest to kodowanie UTF-8, więc zajmowało więcej niż jeden bajt w pamięci komputera. Możemy to udowodnić, wykonując zrzut szesnastkowy:
$ printf ł | hd
który pokazuje:
00000000 c5 82 |..|
Zera są przesunięte. 82
jest pierwszym i c5
jest drugim bajtem reprezentującym ł
(w celu ich odczytania). |..|
jest reprezentacją graficzną, która w tym przypadku nie jest możliwa.
Cóż, jeśli podasz ł
jako dane wejściowe do programu BF, który czyta pojedynczy bajt, pamięć programu będzie wyglądać następująco:
...[0][0][*197*][0][0]...
Dlaczego 197
? Cóż, 197
dziesiętne to c5
szesnastkowe. Wydaje się znajomy? Oczywiście. To pierwszy bajt ł
!
3. Wyjście
Aby wydrukować znak, użyj kropki. .
Co to robi: Zakładając, że traktujemy rzeczywistą wartość komórki jak dziesiętny kod ASCII, wypisz odpowiedni znak na standardowe wyjście.
Cóż, wyobraźmy sobie, że pamięć programu BF wygląda następująco:
...[0][0][*97*][0][0]...
Jeśli teraz używasz operatora kropki (.), BF wypisuje:
za
Ponieważ a
kod dziesiętny w ASCII to 97
.
Na przykład program BF taki jak ten (97 plus 2 kropki):
+++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++.
Zwiększy wartość wskazywanej komórki do 97 i wydrukuje ją 2 razy.
aa
4. Pętle
W pętli BF składa się z początku [
i końca pętli ]
. Możesz pomyśleć, że to tak, jak w C / C ++, gdzie warunek jest rzeczywistą wartością komórki.
Spójrz na program BF poniżej:
++[]
++
zwiększa dwukrotnie rzeczywistą wartość komórki:
...[0][0][*2*][0][0]...
I []
jest tak while(2) {}
, więc to nieskończona pętla.
Powiedzmy, że nie chcemy, aby ta pętla była nieskończona. Możemy na przykład:
++[-]
Tak więc za każdym razem, gdy pętla się zapętla, zmniejsza rzeczywistą wartość komórki. Gdy aktualna wartość komórki się 0
kończy:
...[0][0][*2*][0][0]... loop starts
...[0][0][*1*][0][0]... after first iteration
...[0][0][*0*][0][0]... after second iteration (loop ends)
Rozważmy jeszcze jeden przykład skończonej pętli:
++[>]
Ten przykład pokazuje, że nie kończyliśmy pętli w komórce, w której pętla się rozpoczęła:
...[0][0][*2*][0][0]... loop starts
...[0][0][2][*0*][0]... after first iteration (loop ends)
Jednak dobrą praktyką jest zakończenie tam, gdzie zaczęliśmy. Czemu ? Ponieważ jeśli pętla kończy inną komórkę, to nie możemy założyć, gdzie będzie wskaźnik komórki. Szczerze mówiąc, ta praktyka sprawia, że mózg jest mniejszy.