Brainfuck Interpreter - problem z zagnieżdżonymi pętlami

0

Witam

Od paru dni męczę się z implementacją algorytmu dotyczącego zagnieżdżonych pętli w Brainfucku. Napisałem program, który "interpretuje" kod Brainfucka, ale bez uwzględniania zagnieżdżonych pętli. Chciałbym żeby interpreter był jak najlepiej dopracowany dlatego proszę Was o pomoc. Problem przedstawię dość szczegółowo, aby nic nie zabrakło. To jest cały kod programu:

import sys


def execute(filename):
    f = open(filename, 'r')
    translate(f.read())
    f.close()


def translate(commands):

    bf_array, cell_pointer, bf_code_pointer = [0], 0, 0,
    bf_code = list(commands)
    tmp_bfstack = []        # pozycje nawiasow

    while bf_code_pointer < len(bf_code):
        if bf_code[bf_code_pointer] == '>':
            bf_array.append(0)
            cell_pointer += 1
        if bf_code[bf_code_pointer] == '<':
            cell_pointer -= 1
        if bf_code[bf_code_pointer] == '+':
            bf_array[cell_pointer] += 1
        if bf_code[bf_code_pointer] == '-':
            bf_array[cell_pointer] -= 1
        if bf_code[bf_code_pointer] == '.': print(chr(bf_array[cell_pointer]))
        if bf_code[bf_code_pointer] == ',': bf_array[cell_pointer] = ord(input())
        if bf_code[bf_code_pointer] == '[':
            tmp_bfstack.append(bf_code_pointer)
            print(tmp_bfstack)                # TEST
        if bf_code[bf_code_pointer] == ']' and bf_array[cell_pointer] != 0:
            bf_code_pointer = tmp_bfstack.pop()

        bf_code_pointer += 1
        print(bf_code_pointer)               # TEST


def main():
    if len(sys.argv) == 2: execute(sys.argv[1])
    else: print('Try:', sys.argv[0], 'filename.')


main()


W tym fragmencie jest problem:

 if bf_code[bf_code_pointer] == '[':
            tmp_bfstack.append(bf_code_pointer)
            print(tmp_bfstack)                # TEST
 if bf_code[bf_code_pointer] == ']' and bf_array[cell_pointer] != 0:
            bf_code_pointer = tmp_bfstack.pop()

 bf_code_pointer += 1

Mianowicie pomyślałem, że program ma działać w następujący sposób:
Do listy dodaje pozycję '[' za każdym razem gdy go znajdzie. Jak wiadomo '[' jest punktem inicjalizującym pętlę, a ']' jest punktem kończącym jej działanie. Toteż logika ma być przykładowo taka: >>+[-->><++] '[' jest na pozycji 3, więc 3 zostaje zapisana do listy tmp_bfstack. Gdy while dotrze do ']' wówczas ustawiamy wskaźnik na pozycję 3 i rozpoczynamy wykonywanie instrukcji między nawiasami do momentu aż wartość bf_array[cell_pointer] będzie równa 0.
Mam nadzieję, że dobrze wytłumaczyłem :)

Oto przykład błędnego działania i przykład programu dla którego kod źle działa:

,>,>++++++++[<------<------>>-]<<[>[>+>+<<-]>>[<<+>>-]<<<-]>>>++++++[<++++++++>-]<.
2
1
2
2
3
4
5
6
7
8
9
10
11
12
[12]
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
13

'[' jest na pozycji 12 natomiast ']' znajduje się na pozycji 30. Powyżej znajdują się wypisane wskaźniki. Wskaźnik po wykryciu ']' powinien wrócić na pozycję 12 zamiast 13 tak jak jest w przykładzie, aby program działał poprawnie.

No więc próbowałem zrobić coś takiego:

 if bf_code[bf_code_pointer] == '[':
            tmp_bfstack.append(bf_code_pointer)
            print(tmp_bfstack)                # TEST
 if bf_code[bf_code_pointer] == ']' and bf_array[cell_pointer] != 0:
            bf_code_pointer = tmp_bfstack.pop() - 1

 bf_code_pointer += 1

bf_code_pointer = tmp_bfstack.pop() - 1 W tym przypadku wskaźnik rzeczywiście wskazuje na poprawną pozycję, ale program się zapętla.

Byłbym wdzięczny za jakąkolwiek pomoc w naprowadzeniu mnie na dobry tor

1

Przy zagnieżdżeniach przydaje się zawsze licznik które zagnieżdżenie powinien wykonywać, a które pamiętać :).
Tego nie unikniesz niestety, to jak z interpretacją znaczników w html'u kiedy się otwierają i kiedy zamykają :P.
Do tego trochę kodu musisz przebudować :)

0
Guaz napisał(a):

Przy zagnieżdżeniach przydaje się zawsze licznik które zagnieżdżenie powinien wykonywać, a które pamiętać :).
Tego nie unikniesz niestety, to jak z interpretacją znaczników w html'u kiedy się otwierają i kiedy zamykają :P.
Do tego trochę kodu musisz przebudować :)

Właśnie tutaj jest mój problem chyba, że nie mam pomysłu jak to zaimplementować. :) Myślałem, że moje rozwiązanie okaże się tym zliczaniem zagnieżdżeń. Bo zobacz, przypuśćmy, że ktoś chciałby dostać wynik działania takiego programu:

>>+++[<<[+++--++<<>]-->]

Szczerze to nie wiem na ile to jest poprawnie w Brainfucku napisane, ten program ma posłużyć tylko jako przykład.
Próbowałem to zrobić właśnie w ten sposób, że

if bf_code[bf_code_pointer] == '['

To dodaj pozycję do listy bf_stack. Czyli w liście bf_stack byłaby najpierw 5 w 0 elemencie, a następnie 8 w 1 elemencie, a wskaźnik przechodziłby dalej.

if bf_code[bf_code_pointer] == ']' and bf_array[cell_pointer] != 0

Jeśli taki warunek byłby prawdziwy, czyli jakby wskaźnik doszedł do ']' to wtedy wskaźnik ma się równać bf_stack.pop(), czyli ustawiam wskaźnik na pozycję 8, a zatem wskaźnik wskazywałby na drugi w kolejności '[' i tak dalej by się to wykonywało. Jakby ta pętla doprowadziła do tego, że bf_array[cell_pointer] == 0
to wskaźnik by się przesunął i natrafiłby na ostatni prawy nawias. Znów zostałaby wykonana instrukcja bf_stack.pop(), a więc wskaźnik wskazywałby na pozycję 5, czyli na pierwszy w kolejności '['.

Myślałem, że ta metoda sama w sobie posiada licznik zagnieżdżeń. @Guaz mógłbyś mi powiedzieć jeszcze gdzie leży błąd w moim pomyśle? :) Nie bardzo rozumiem na czym to zliczanie miałoby polegać.

0

Pewnie, więc prześledźmy logikę wyrażenia. Zignorujmy póki co and, bo przyznam że nie wiem do czego służy - przez to mogę źle interpretować że to bezpiecznik, więc w takim wypadku przepraszam za zbyt daleko wysunięte wnioski.
I spróbujmy to narysować, dolna warstwa to program, kolejne to kierunek wykonywania i 'jumpy'.

{
Napotykając '[' wrzuć pointer(nr znaku interpretowanego) do tmp_stack.
Napotykając ']' za pointer przyjmij ostatnią wartość z tmp_stack wyjmując ją.
}
      >>>>>>>>>>>>R?
      <=======
  >>>>H>>>>>>R
  <================
      >>>>>>>>>>>>R
      <=======
  H   H      R
>>[>>>[>>>>>>]....]..
H - zapis pozycji
R - powrót

Jeśli rozumiesz mój schemat, program finalnie wykona zagnieżdżoną dwukrotnie instrukcje 4 razy, zaś pełną 2 razy.
Więc brakuje nam tu jednego elementu. Decyzyjności, kiedy ma wyjść z tej pętli. Potrzebujesz dodatkowego sprawdzenia, kiedy ma wracać, a kiedy ma kontynuować działanie bo pętle już wykonał określoną ilość razy :).
Nie wiem jak ta decyzyjność w brainfuck'u przebiega, więc tutaj nie jestem w stanie pomóc, ze strony logicznej, tutaj widzę błąd.
''bf_code_pointer = tmp_bfstack.pop() - 1 W tym przypadku wskaźnik rzeczywiście wskazuje na poprawną pozycję, ale program się zapętla.''
Jeśli to pętla, to chyba dobrze żeby się zapętlał :D. Tylko kwestia zinterpretowania wyjścia z pętli. Więc powinna się tu znaleźć instrukcja if :).

0
Guaz napisał(a):

Pewnie, więc prześledźmy logikę wyrażenia. Zignorujmy póki co and, bo przyznam że nie wiem do czego służy - przez to mogę źle interpretować że to bezpiecznik, więc w takim wypadku przepraszam za zbyt daleko wysunięte wnioski.
I spróbujmy to narysować, dolna warstwa to program, kolejne to kierunek wykonywania i 'jumpy'.

{
Napotykając '[' wrzuć pointer(nr znaku interpretowanego) do tmp_stack.
Napotykając ']' za pointer przyjmij ostatnią wartość z tmp_stack wyjmując ją.
}
      >>>>>>>>>>>>R?
      <=======
  >>>>H>>>>>>R
  <================
      >>>>>>>>>>>>R
      <=======
  H   H      R
>>[>>>[>>>>>>]....]..
H - zapis pozycji
R - powrót

Jeśli rozumiesz mój schemat, program finalnie wykona zagnieżdżoną dwukrotnie instrukcje 4 razy, zaś pełną 2 razy.
Więc brakuje nam tu jednego elementu. Decyzyjności, kiedy ma wyjść z tej pętli. Potrzebujesz dodatkowego sprawdzenia, kiedy ma wracać, a kiedy ma kontynuować działanie bo pętle już wykonał określoną ilość razy :).
Nie wiem jak ta decyzyjność w brainfuck'u przebiega, więc tutaj nie jestem w stanie pomóc, ze strony logicznej, tutaj widzę błąd.
''bf_code_pointer = tmp_bfstack.pop() - 1 W tym przypadku wskaźnik rzeczywiście wskazuje na poprawną pozycję, ale program się zapętla.''
Jeśli to pętla, to chyba dobrze żeby się zapętlał :D. Tylko kwestia zinterpretowania wyjścia z pętli. Więc powinna się tu znaleźć instrukcja if :).

To jest dość ciekawe, bo program zapętla się tylko w jednym miejscu, Tak jakby reszta pętli przeszła w pełni poprawnie, a ta jedna miałaby w sobie błąd.

46
47
48
49
50
51
52
53
-------------------------------------------
46
47
48
49
50
51
52
53
--------------------------------------------
46
47
48
49
50
51
52
53
--------------------------------------------
46
47
48
49
50
51
52
53
--------------------------------------------

Powyżej znajdują się pozycje wskaźników. Chodzi dokładnie o ten fragment programu w Brainfucku:

[>[>+>+<<-]>>[<<+>>-]<<<-]

W tych zagnieżdżonych pętlach jest błąd, a konkretniej w drugiej z nich:

[<<+>>-]

Lewy nawias jest na pozycji 46, a prawy na 53, a więc inkrementacja wskaźników działa poprawnie, ale ta inkrementacja niestety wykonuje się w nieskończoność, tak jakby ten wskazywany element tablicy nigdy nie mógł równać się 0.

1

@Shizzer: Twoje komentarze pod moją odpowiedzią, skłoniły mnie do szybkiego zapoznania się z brainfuck'iem, napisałem w sumie z palca prosty interpreter i działa nawet dla zagnieżdżeń. Więc pozwól że odpowiem po prostu kodem :)
[Równie szybko zainteresowanie przeszło, ale chwila zabawy była :D]

class Interpreter:
    def __inc_ptr(self):  self.pointer+=1; self.cells.append(0) if len(self.cells)<=self.pointer else None;
    def __dec_ptr(self):  self.pointer-=1;
    def __inc_val(self):  self.cells[self.pointer]+=1;
    def __dec_val(self):  self.cells[self.pointer]-=1;
    def __put_char(self): print(chr(self.cells[self.pointer]));
    def __get_char(self): self.cells[self.pointer]=ord(input("wczytaj znak:"));
    def __beg_while(self):self.stack.append(self.pos);
    def __end_while(self):
        if self.cells[self.pointer] != 0:
            self.pos = self.stack[-1]
        else:
            self.stack.pop()

    def __init__(self):
        self.fct = {">": self.__inc_ptr,   \
                    "<": self.__dec_ptr,   \
                    "+": self.__inc_val,   \
                    "-": self.__dec_val,   \
                    ".": self.__put_char,  \
                    ",": self.__get_char,  \
                    "[": self.__beg_while, \
                    "]": self.__end_while  \
                    }


    def execute(self, code):
        self.cells = [0]
        self.pointer = 0
        self.stack = []
        self.pos = 0
        while self.pos < len(code):
            try:
                self.fct.get(code[self.pos])()
            except TypeError:
                pass #Pseudo zabezpieczenie przed niepożądanymi znakami typu spacje, opisy itd.
            finally:
                #~ print(code[self.pos], self.stack, self.pointer, self.cells) #Debug
                self.pos += 1

def main():
    a = Interpreter()
    a.execute(",>,>++++++++[<------<------>>-]<<[>[>+>+<<-]>>[<<+>>-]<<<-]>>>++++++[<++++++++>-]<.")
    a.execute(",>,< [ > [ >+ >+ << -] >> [- << + >>] <<< -] >>")

if __name__ == "__main__":
    main()

Z klasą to może lekki przerost formy nad treścią, ale potrzebowałem parametru self do funkcji :).
Jakoś taka pętla z referencjami do funkcji wydaje mi się czytelniejsza :p.

Ale przechodząc do konkretu, miałeś rację co do fragmentu w którym się myliłeś, tylko zabrakło ci jednej instrukcji.
Jeśli nasz znak w kodzie jest ']' i aktualnie wskazywana komórka jest zerem, to wtedy musi iść dalej, olewając ten znak (wyjście z pętli) i usuwając ostatnio odłożony indeks.
W przeciwnym wypadku ma pobrać ostatnio odłożoną wartość nawiasu ale nie kasując jej jak to robisz za pomocą .pop.

I jak w sumie nadmieniałem (ale na ślepo strzelając) .pop()+1 jest błędem. Wystarczy pozbawić linię +1, oraz zrobić poprawne wyjście z pętli.
Wtedy napotykając znak '[' do którego się przeniesiemy, znów go dodamy. (chociaż to sztuka dla sztuki, ale myślenie dobre)

@Edit:
Mała poprawka kodu na poprawnie napisany.

@Edit2:
Dla szybkości polecam sprawdzić czy szybszy jest try/sprawdzenie czy znak znajduje się w zakresie dozwolonych/pozostawienie u znaków dozwolonych przed pętlą wykonującą.
Wydaje mi się że ostatnia opcja:
code = "".join(i for i in code if i in self.fct)

0

Tak, ale to jest albo błąd definicji, albo niedoprecyzowanie.
Zauważ że wczytujemy liczbę jako znak ascii o określonym numerze.
Jeśli cyfry powinny oznaczać co reprezentują, musimy zmienić funkcję: def __put_char(self): print(chr(self.cells[self.pointer]), end="");
W innym wypadku powinno nam zwrócić znak reprezentowany przez liczbę w ascii 2 (bo spacja dzieli z resztą dwa razy "A").

wczytaj znak:A
, [] 0 [65]
> [] 1 [65, 0]
wczytaj znak: #<= tu jest spacja
, [] 1 [65, 32]
> [] 2 [65, 32, 0]
+ [] 2 [65, 32, 1]
+ [] 2 [65, 32, 2]
+ [] 2 [65, 32, 3]
+ [] 2 [65, 32, 4]
+ [] 2 [65, 32, 5]
+ [] 2 [65, 32, 6]
[ [10] 2 [65, 32, 6]
- [10] 2 [65, 32, 5]
< [10] 1 [65, 32, 5]
- [10] 1 [65, 31, 5]
- [10] 1 [65, 30, 5]
- [10] 1 [65, 29, 5]
- [10] 1 [65, 28, 5]
- [10] 1 [65, 27, 5]
- [10] 1 [65, 26, 5]
- [10] 1 [65, 25, 5]
- [10] 1 [65, 24, 5]
< [10] 0 [65, 24, 5]
- [10] 0 [64, 24, 5]
- [10] 0 [63, 24, 5]
- [10] 0 [62, 24, 5]
- [10] 0 [61, 24, 5]
- [10] 0 [60, 24, 5]
- [10] 0 [59, 24, 5]
- [10] 0 [58, 24, 5]
- [10] 0 [57, 24, 5]
> [10] 1 [57, 24, 5]
> [10] 2 [57, 24, 5]
[ [10] 2 [57, 24, 5]
- [10] 2 [57, 24, 4]
< [10] 1 [57, 24, 4]
- [10] 1 [57, 23, 4]
- [10] 1 [57, 22, 4]
- [10] 1 [57, 21, 4]
- [10] 1 [57, 20, 4]
- [10] 1 [57, 19, 4]
- [10] 1 [57, 18, 4]
- [10] 1 [57, 17, 4]
- [10] 1 [57, 16, 4]
< [10] 0 [57, 16, 4]
- [10] 0 [56, 16, 4]
- [10] 0 [55, 16, 4]
- [10] 0 [54, 16, 4]
- [10] 0 [53, 16, 4]
(...)
< [10] 0 [41, 0, 2]
- [10] 0 [40, 0, 2]
- [10] 0 [39, 0, 2]
- [10] 0 [38, 0, 2]
- [10] 0 [37, 0, 2]
- [10] 0 [36, 0, 2]
- [10] 0 [35, 0, 2]
- [10] 0 [34, 0, 2]
- [10] 0 [33, 0, 2]
> [10] 1 [33, 0, 2]
> [10] 2 [33, 0, 2]
[ [10] 2 [33, 0, 2]
- [10] 2 [33, 0, 1]
< [10] 1 [33, 0, 1]
- [10] 1 [33, -1, 1]
- [10] 1 [33, -2, 1]
- [10] 1 [33, -3, 1]

Natomiast patrząc na to jak się wykonuje kod, Nasza pętla pomimo wskaźnika równego 0, zaczyna dekrementację i będzie dekrementować w nieskończoność wykonując jeszcze kilka operacji pobocznych...
Jedyne co pozostaje, to sprawdzić jak się wykonuje kod w prawdziwym interpreterze. Bo według logiki powinna zwrócić znak o numerze 2 i kod jest spaprany o czym świadczy dekrementacja poniżej zakresu. Ale sprawdzę to w najbliższych dniach lub tej nocy, aktualnie już nie mam czasu by podłubać :).

0

Na razie poprawiłem kod uwzględniając w nim wyjście poza zakres bitów reprezentujących znaki ascii w operacjach arytmetycznych:

#!/usr/bin/python3

import sys


def execute(filename):
    f = open(filename, 'r')
    translate(f.read())
    f.close()


def translate(commands):

    bf_array, cell_pointer, bf_code_pointer = [0], 0, 0
    bf_code = list(commands)
    tmp_stack = []        # pozycje nawiasow

    while bf_code_pointer < len(bf_code):

        if bf_code[bf_code_pointer] == '>':
            cell_pointer += 1
            if cell_pointer == len(bf_array): bf_array.append(0)

        if bf_code[bf_code_pointer] == '<':
            if cell_pointer <= 0:
                cell_pointer = 0
            else:
                cell_pointer -= 1

        if bf_code[bf_code_pointer] == '+':
           if bf_array[cell_pointer] < 255:
                bf_array[cell_pointer] += 1
           else:
               bf_array[cell_pointer] = 0

        if bf_code[bf_code_pointer] == '-':
            if bf_array[cell_pointer] > 0:
                bf_array[cell_pointer] -= 1
            else:
                bf_array[cell_pointer] = 255

        if bf_code[bf_code_pointer] == '.': print(chr(bf_array[cell_pointer]))

        if bf_code[bf_code_pointer] == ',': bf_array[cell_pointer] = ord(input('Wczytaj znak: '))

        if bf_code[bf_code_pointer] == '[':
            tmp_stack.append(bf_code_pointer)

        if bf_code[bf_code_pointer] == ']' and bf_array[cell_pointer] != 0:
            bf_code_pointer = tmp_stack[-1]

        if bf_code[bf_code_pointer] == ']' and bf_array[cell_pointer] == 0:
            tmp_stack.pop()

        bf_code_pointer += 1


def main():
    if len(sys.argv) == 2: execute(sys.argv[1])
    else: print('Try:', sys.argv[0], 'filename.')


main()

Natomiast nadal występuje problem zapętlania.

[3, 0, 136, 0, 121, 0, 0]
[3, 0, 136, 0, 121, 0, 0]
[3, 0, 136, 0, 121, 0, 0]
[3, 0, 136, 0, 121, 0, 0]
[3, 0, 136, 0, 120, 0, 0]
[3, 0, 136, 0, 120, 0, 0]
[3, 0, 136, 0, 120, 0, 0]
[3, 0, 137, 0, 120, 0, 0]
[3, 0, 137, 0, 120, 0, 0]
[3, 0, 137, 0, 120, 0, 0]
[3, 0, 137, 0, 120, 0, 0]
[3, 0, 137, 0, 119, 0, 0]
[3, 0, 137, 0, 119, 0, 0]
[3, 0, 137, 0, 119, 0, 0]
[3, 0, 138, 0, 119, 0, 0]
[3, 0, 138, 0, 119, 0, 0]
[3, 0, 138, 0, 119, 0, 0]
[3, 0, 138, 0, 119, 0, 0]
[3, 0, 138, 0, 118, 0, 0]
[3, 0, 138, 0, 118, 0, 0]
[3, 0, 138, 0, 118, 0, 0]
[3, 0, 139, 0, 118, 0, 0]
[3, 0, 139, 0, 118, 0, 0]
[3, 0, 139, 0, 118, 0, 0]
[3, 0, 139, 0, 118, 0, 0]
[3, 0, 139, 0, 117, 0, 0]
[3, 0, 139, 0, 117, 0, 0]
[3, 0, 139, 0, 117, 0, 0]
[3, 0, 140, 0, 117, 0, 0]
[3, 0, 140, 0, 117, 0, 0]
[3, 0, 140, 0, 117, 0, 0]
[3, 0, 140, 0, 117, 0, 0]
[3, 0, 140, 0, 116, 0, 0]
[3, 0, 140, 0, 116, 0, 0]
[3, 0, 140, 0, 116, 0, 0]
[3, 0, 141, 0, 116, 0, 0]
[3, 0, 141, 0, 116, 0, 0]
[3, 0, 141, 0, 116, 0, 0]
[3, 0, 141, 0, 116, 0, 0]
[3, 0, 141, 0, 115, 0, 0]
[3, 0, 141, 0, 115, 0, 0]
[3, 0, 141, 0, 115, 0, 0]
[3, 0, 142, 0, 115, 0, 0]
[3, 0, 142, 0, 115, 0, 0]
[3, 0, 142, 0, 115, 0, 0]
[3, 0, 142, 0, 115, 0, 0]
[3, 0, 142, 0, 114, 0, 0]
[3, 0, 142, 0, 114, 0, 0]
[3, 0, 142, 0, 114, 0, 0]
[3, 0, 143, 0, 114, 0, 0]
[3, 0, 143, 0, 114, 0, 0]
[3, 0, 143, 0, 114, 0, 0]
[3, 0, 143, 0, 114, 0, 0]
[3, 0, 143, 0, 113, 0, 0]
[3, 0, 143, 0, 113, 0, 0]
[3, 0, 143, 0, 113, 0, 0]
[3, 0, 144, 0, 113, 0, 0]
[3, 0, 144, 0, 113, 0, 0]
(...)

Przy cyfrach 4 i 2, które po podzieleniu oczywiście powinny dać cyfrę 2 wypisują na ekran 0 i to 0 jest wypisaywane dla każdej innej pary liczb

1

Zerknąłem już jakiś czas temu na opis kodu i widzę że w ogóle źle rozumiałem, to miało tylko dzielić liczby :P.
>[-<<- odejmujemy 1 od dzielnej (0) i dzielnika (2) dopóki (2) nie jest 0
W tym miejscu wysypuje się interpreter. I jest to błąd ponieważ zamiast w pętli odejmować od dzielnej dzielnika, przerzuca nam się na inne zagnieżdżenie. przez co przeskakują nam nieprawidłowo wskaźniki.
Ale jak to poprawić, to spróbuję na tygodniu, to wskazówka jeśli się będziesz z tym bawił w międzyczasie :)

@Edit:
",>,>++++++[-<--------<-------->>]<<[>[>+>+<<-]>[-<<->>]>>+<[-<<+>>]<<<]>>>++++++[->++++++++<]>."
A ten kod działa dla dzielenia całkowitego (9 i 3, oraz 4 i 2) z zabezpieczeniem dla wartości ujemnych, powinno też dla innych wartości :).
Z tym że wyjdzie zapewne wtedy 9/2 == 5 zaokrąglane w górę.
Ale to problem był z kodem z wiki, nie interpreterem tak jak sądziłem wyżej.

0
Guaz napisał(a):

Zerknąłem już jakiś czas temu na opis kodu i widzę że w ogóle źle rozumiałem, to miało tylko dzielić liczby :P.
>[-<<- odejmujemy 1 od dzielnej (0) i dzielnika (2) dopóki (2) nie jest 0
W tym miejscu wysypuje się interpreter. I jest to błąd ponieważ zamiast w pętli odejmować od dzielnej dzielnika, przerzuca nam się na inne zagnieżdżenie. przez co przeskakują nam nieprawidłowo wskaźniki.
Ale jak to poprawić, to spróbuję na tygodniu, to wskazówka jeśli się będziesz z tym bawił w międzyczasie :)

@Edit:
",>,>++++++[-<--------<-------->>]<<[>[>+>+<<-]>[-<<->>]>>+<[-<<+>>]<<<]>>>++++++[->++++++++<]>."
A ten kod działa dla dzielenia całkowitego (9 i 3, oraz 4 i 2) z zabezpieczeniem dla wartości ujemnych, powinno też dla innych wartości :).
Z tym że wyjdzie zapewne wtedy 9/2 == 5 zaokrąglane w górę.
Ale to problem był z kodem z wiki, nie interpreterem tak jak sądziłem wyżej.

Wszystko ładnie działa. Sprawdzałem kody Brainfucka wprowadzając dane do mojego interpretera i do interpretera online - wyniki są takie same.

Dzięki wielkie za pomoc :)

Jeszcze bardzo by było miło jakbyś napisał czy taki kod jest wystarczający czy można go jakoś ładniej napisać:

#!/usr/bin/python3

import sys


def execute(filename):
    f = open(filename, 'r')
    translate(f.read())
    f.close()


def translate(commands):

    bf_array, cell_pointer, bf_code_pointer = [0], 0, 0
    bf_code = list(commands)
    tmp_stack = []        # pozycje nawiasow

    while bf_code_pointer < len(bf_code):

        if bf_code[bf_code_pointer] == '>':
            cell_pointer += 1
            if cell_pointer == len(bf_array): bf_array.append(0)

        if bf_code[bf_code_pointer] == '<':
            if cell_pointer <= 0:
                cell_pointer = 0
            else:
                cell_pointer -= 1

        if bf_code[bf_code_pointer] == '+':
           if bf_array[cell_pointer] < 255:
                bf_array[cell_pointer] += 1
           else:
               bf_array[cell_pointer] = 0

        if bf_code[bf_code_pointer] == '-':
            if bf_array[cell_pointer] > 0:
                bf_array[cell_pointer] -= 1
            else:
                bf_array[cell_pointer] = 255

        if bf_code[bf_code_pointer] == '.': print(chr(bf_array[cell_pointer]))

        if bf_code[bf_code_pointer] == ',': bf_array[cell_pointer] = ord(input('Wczytaj znak: '))

        if bf_code[bf_code_pointer] == '[':
            tmp_stack.append(bf_code_pointer)

        if bf_code[bf_code_pointer] == ']' and bf_array[cell_pointer] != 0:
            bf_code_pointer = tmp_stack[-1]

        if bf_code[bf_code_pointer] == ']' and bf_array[cell_pointer] == 0:
            tmp_stack.pop()

        bf_code_pointer += 1


def main():
    if len(sys.argv) == 2: execute(sys.argv[1])
    else: print('Try:', sys.argv[0], 'filename.')


main()

Zależy mi na tym, żeby kod był jak najlepszy, ale jeszcze mało wiem na temat 'czystego kodu'. Wiem, że jest książka na ten temat i zapewne niedługo trafi na moją półkę :)

0

Kiedyś miałem problem odnośnie resetowania wartości zmiennych gdy działam na funkcjach. Chciałem użyć słowników zamiast tych if'ów właśnie i niestety, ale znów nastąpił problem w kontekście resetowania wartości. Nie umiem jeszcze programowania objektowego, a nie chcę kopiować Twojego kodu, ale wiem, że Ty mogłeś zastosować słowniki i nie miałeś problemów z resetowaniem wartości zmiennych, bo miałeś do dyspozycji self. Jak mógłbym zastosować słowniki przy kodzie opartym na funkcjach?

#!/usr/bin/python3

import sys


def execute(filename):
    f = open(filename, 'r')
    translate(f.read())
    f.close()


def translate(commands):

    cells, cell_pointer, code_pointer = [0], 0, 0
    bf_code = list(commands)
    stack = []

    def _inc_ptr(cell_pointer, cells):
        cell_pointer += 1
        if cell_pointer == len(cells):
            cells.append(0)

    def _dec_ptr(cell_pointer, cells):
        cell_pointer -= 1
        if cell_pointer <= 0:
            cell_pointer = 0

    def _increment_value(cells, cell_pointer):
        cells[cell_pointer] = 0
        if cells[cell_pointer] < 255:
            cells[cell_pointer] += 1

    def _decrement_value(cells, cell_pointer):
        cells[cell_pointer] = 0
        if cells[cell_pointer] > 0:
            cells[cell_pointer] -= 1

    def _put_char(cells, cell_pointer):
        print(chr(cells[cell_pointer]))

    def _get_char(cells, cell_pointer):
        cells[cell_pointer] = ord(input("Write a sign: "))

    def _begin_loop(stack, code_pointer):
        stack.append(code_pointer)

    def _end_loop(code_pointer, stack, cells, cell_pointer):
        if cells[cell_pointer] != 0:
            code_pointer = stack[-1]
        else:
            stack.pop()

    signs = {   ">": _inc_ptr(cell_pointer, cells),
                "<": _dec_ptr(cell_pointer, cells),
                "+": _increment_value(cells, cell_pointer),
                "-": _decrement_value(cells, cell_pointer),
                ".": _put_char(cells, cell_pointer),
                ",": _get_char(cells, cell_pointer),
                "[": _begin_loop(stack, code_pointer),
                "]": _end_loop(code_pointer, stack, cells, cell_pointer)
            }

    while code_pointer < len(bf_code):
        try:
            signs.get(bf_code[code_pointer])
        finally:
            code_pointer += 1


def main():
        if len(sys.argv) == 2:
            execute(sys.argv[1])
        else:
            print('Try:', sys.argv[0], 'filename.')


main()

0

Być może ten post pomoże kiedyś komuś kto też będzie szukał rozwiązania podobnego problemu :)

Problem inkrementowania zmiennych globalnych przy użyciu funkcji został rozwiązany i był dość trywialny, aż wstyd, że dopiero teraz wpadł mi do głowy. :)
Mianowicie zmienne globalne nie inkrementowały się poprawnie, ponieważ w funkcjach rzecz jasna działamy na kopiach zmiennych, a nie na ich oryginalnych postaciach. W pythonie istnieje sposób na zaimplementowanie działania w funkcji na oryginałach zmiennych globalnych, a służy do tego global.

Oto przykład:

  count = 0
   def counter(): 
..     global count 
..     count += 1 
..     return count 
..     
   counter()
=> 1
   counter()
=> 2
   counter()
=> 3

Jak widać można działać na oryginałach zmiennych nawet wewnątrz funkcji.

1

Jest kilka sposobów:
#1 Argumenty domyślnie przekazywane do funkcji w słowniku przy wywołaniu go metodą get (tylko musisz wtedy zastosować lambdę, aby to było głębokie wywołanie, inaczej ci się wywołają w trakcie inicjalizacji słownika gdy dasz mu nawiasy wywołujące, tu jest jednak zagadka jak przypisać to do odpowiedniej zmiennej lokalnej w funkcji rodzicielskiej. w tym powinna ci pomóc ''globalność'' słowników.
https://stackoverflow.com/questions/14323817/global-dictionaries-dont-need-keyword-global-to-modify-them
I ta opcja jest zdecydowanie najlepsza, bo nie wykorzystujesz globalnych nazw gdybyś zaimportował ten słownik w jakimś programie przypadkiem poprzez instrukcję from plik import *
A przy okazji wszystkie zmienne masz zmagazynowane według identyfikatorów w jednym zbiorze, bo nadal jest to integralna część funkcji powstająca zawsze.
#2 Druga opcja, to zmienne globalne i jak wyżej, tylko bez przekazywania argumentów domyślnych. global jest jednak odradzaną przeze mnie metodą, powyższa robi pseudoglobalną.
#3 Jest jeszcze trzecia opcja, w której wykorzystujesz metodę __call__, to jest jednak już nieco bardziej zakrawające o obiektowość.

0

Dla ciekawskich pseudoglobalności, czy raczej 'referencji' słowników na tym przykładzie, przy programowaniu funkcyjnym:

class Interpreter:
    def __inc_ptr(self):  self.pointer+=1; self.cells.append(0) if len(self.cells)<=self.pointer else None;
    def __dec_ptr(self):  self.pointer-=1;
    def __inc_val(self):  self.cells[self.pointer]+=1;
    def __dec_val(self):  self.cells[self.pointer]-=1;
    def __put_char(self): print(chr(self.cells[self.pointer]), end="");
    def __get_char(self): self.cells[self.pointer]=ord(input("wczytaj znak:"));
    def __beg_while(self):self.stack.append(self.pos);
    def __end_while(self):
        if self.cells[self.pointer] > 0:
            self.pos = self.stack[-1]
        else:
            self.stack.pop()

    def __init__(self):
        self.fct = {">": self.__inc_ptr,   \
                    "<": self.__dec_ptr,   \
                    "+": self.__inc_val,   \
                    "-": self.__dec_val,   \
                    ".": self.__put_char,  \
                    ",": self.__get_char,  \
                    "[": self.__beg_while, \
                    "]": self.__end_while  \
                    }


    def execute(self, code):
        self.cells = [0]
        self.pointer = 0
        self.stack = []
        self.pos = 0
        code = "".join(i for i in code if i in self.fct)
        while self.pos < len(code):
            self.fct.get(code[self.pos])()
            self.pos += 1

def interpreter(code):
    def __inc_ptr(D):  D["pointer"] += 1; D["cells"].append(0) if len(D["cells"]) <= D["pointer"] else None;
    def __dec_ptr(D):  D["pointer"] -= 1;
    def __inc_val(D):  D["cells"][D["pointer"]]+=1;
    def __dec_val(D):  D["cells"][D["pointer"]]-=1;
    def __put_char(D): print(chr(D["cells"][D["pointer"]]), end="");
    def __get_char(D): D["cells"][D["pointer"]]=ord(input("wczytaj znak:"));
    def __beg_while(D): D["stack"].append(D["pos"]);
    def __end_while(D):
        if D["cells"][D["pointer"]] > 0:
            D["pos"] = D["stack"][-1]
        else:
            D["stack"].pop()

    data = {"pointer": 0,
            "cells": [0],
            "stack": [],
            "pos": 0
            }

    signs = {   ">": __inc_ptr,
                "<": __dec_ptr,
                "+": __inc_val,
                "-": __dec_val,
                ".": __put_char,
                ",": __get_char,
                "[": __beg_while,
                "]": __end_while
            }

    code = "".join(i for i in code if i in signs)
    while data["pos"] < len(code):
        signs.get(code[data["pos"]])(data)
        data["pos"] += 1


def main():
    while True:
        interpreter(",>,>++++++[-<--------<-------->>]<<[>[>+>+<<-]>[-<<->>]>>+<[-<<+>>]<<<]>>>++++++[->++++++++<]>.")
        a = Interpreter()
        a.execute(",>,>++++++[-<--------<-------->>]<<[>[>+>+<<-]>[-<<->>]>>+<[-<<+>>]<<<]>>>++++++[->++++++++<]>.")

if __name__ == "__main__":
    main()

Czytelność kodu wzrasta. ale prędkość i sens nie używania obiektowości, niestety spadają ;p.

1 użytkowników online, w tym zalogowanych: 0, gości: 1