Brainfuck Interpreter

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Aus irgendeinem Grund passiert nichts, es lauft die ganze Zeit und gibt nichts aus. Was hab ich falsch gemacht?

Code: Alles auswählen

def brainfucker(code,index=0,zellen=[0]*30000):
    code=code
    zellen=zellen
    index=index
    for t in code:
        if t==">":
            index +=1
            
        elif t=="<":
            index -=1
            
        elif t=="+":
            zellen[index] +=1

        elif t=="-":
            zellen[index] -=1

        elif t==".":
            print(zellen[index], end="")

        elif t==",":
            zellen[index] = ord(input())

        elif t=="[":
            while zellen[index] != 0:
                brainfucker(code[code.index(t):code.find("]", code.index(t))],index,zellen)
    
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

fail hat geschrieben:Aus irgendeinem Grund passiert nichts, es lauft die ganze Zeit und gibt nichts aus. Was hab ich falsch gemacht?
Du rufst die Funktion nicht auf. Was soll eigentlich dieses code=code und zellen=zellen?
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

liegt nicht an dem

Code: Alles auswählen

def brainfucker(code,index=0,zellen=[0]*30000):
    for t in code:
        if t==">":
            index +=1
            
        elif t=="<":
            index -=1
            
        elif t=="+":
            zellen[index] +=1

        elif t=="-":
            zellen[index] -=1

        elif t==".":
            print(zellen[index], end="")

        elif t==",":
            zellen[index] = ord(input())

        elif t=="[":
            while zellen[index] != 0:
                brainfucker(code[code.index(t):code.find("]", code.index(t))],index,zellen)
                
                
brainfucker("""++++++++++
 [
  >+++++++>++++++++++>+++>+<<<<-
 ]                       Schleife zur Vorbereitung der Textausgabe
 >++.                    Ausgabe von 'H'
 >+.                     Ausgabe von 'e'
 +++++++.                'l'
 .                       'l'
 +++.                    'o'
 >++.                    Leerzeichen
 <<+++++++++++++++.      'W'
 >.                      'o'
 +++.                    'r'
 ------.                 'l'
 --------.               'd'
 >+.                     '!'
 >.                      Zeilenvorschub
 +++.                    Wagenrücklauf""")
    
BlackJack

@fail: Hast Du selbst denn schon mal versucht mit ``print``-Anweisungen heraus zu finden bis wohin die Ausführung kommt, oder wo sie hängen bleibt? Wenn `code` einfach nur '.' ist, dann wird auch nichts ausgegeben? Hast Du mal systematisch verschiedene BF-Programme geschrieben um die einzelnen Befehle zu testen?

Deine Behandlung von '[' ist falsch. Zum einen findet der ``code.index(t)``-Aufruf immer nur die erste '[' im `code`, also jedes BF-Programm mit mehr als einem '[' wird nicht funktionieren.

Wenn dort die richtige Klammer gefunden *würde*, dann hast Du auch noch das Problem, dass der „Rücksprung” bei Dir den Datenzeiger zurücksetzt, was nicht sein darf. Ich würde an Deiner Stelle die Rekursion weg lassen und einen expliziten Programmzeiger einführen und mit einer Liste einen Aufrufstapel implementieren.

Veränderbare Default-Werte sind gefährlich. Der Wert für `zellen` wird nur *einmal* ausgewertet wenn das ``def`` für die Funktion ausgeführt wird und *nicht* jedes mal wenn man die Funktion aufruft! Das heisst wenn man die Funktion mehrfach nur mit `code` aufruft, dann wird nicht etwas jedes mal mit einem mit Nullen gefüllten Speicher gearbeitet, sondern die `zellen` enthalten die Werte vom letzten Durchlauf.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Eine Frage:

Code: Alles auswählen

for t in "string":
und t ist beim 3 Durchlauf und ist bei "r"
kann ich t wieder auf z.B. "s" setzen oder muss ich das mit einer while schleife machen?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

fail hat geschrieben: kann ich t wieder auf z.B. "s" setzen oder muss ich das mit einer while schleife machen?
Nein Du kannst das nicht wieder zurücksetzen. Aber was meinst Du mit while-Schleife?

Generell gefällt mir diese ``if...elif``-Kaskade nicht. So etwas kann man idR. vermeiden, indem man ein Dispatching definiert, welches vom Wert in der Bedingung auf ein Callable mappt. In Deinem Falle also z.B. so:

Code: Alles auswählen

command_mapping = {
    "<": move_right,
    ">": move_left,
    "+": inc,
    "-": dec,
    # usw...
}

# und dann als Aufruf:
command_mapping[t](index, zellen)
Die Werte im Dictionary sind eben zuvor definierte Callables (also Funktionsobjekte, Lambdas usw).
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

mach ich dann beim refactoring jetzt hab ich schon so angefangen
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

habe immernoch ein problem mit den schleifen

Code: Alles auswählen

def brainfucker(code):
    cellindex=0
    cells=[0]*30000
    codeindex=1
    stack_codeindex=[]
    stack_cellindex=[]
    while codeindex <= len(code):
        if code[codeindex]==">":
            cellindex +=1
            codeindex +=1
            print("vorne")
            
        elif code[codeindex]=="<":
            cellindex -=1
            codeindex +=1
            print("hinten")
            
        elif code[codeindex]=="+":
            cells[cellindex] +=1
            codeindex +=1
            print("inkrementieren")

        elif code[codeindex]=="-":
            cells[cellindex] -=1
            codeindex +=1
            print("dekrementieren")

        elif code[codeindex]==".":
            print(cells[cellindex], end="")
            codeindex +=1
            print("output")

        elif code[codeindex]==",":
            cells[cellindex] = ord(input())
            codeindex +=1
            print("input")

        elif code[codeindex]=="[":
            stack_codeindex.append(codeindex)
            stack_cellindex.append(cellindex)
            codeinex +=1
            print("schleifenanfang")

        elif code[codeindex]=="]":
            last=stack_cellindex[-1]
            if last!= 0:
                codeindex=stack_codeindex[-1]

            else:
                stack_cellindex.pop
                stack_codeindex.pop
                codeindex +=1
            print("schleifenende")
            
            
                
                
BlackJack

@fail: Jetzt baust Du mit dem `stack_cellindex` den gleichen Fehler nach, den Du vorher durch den rekursiven Aufruf hattest. Weder '[' noch ']' verändern den `cellindex`. Du brauchst dafür keinen Stack!

Und beim `stack_codeindex` nimmst Du die Elemente nicht wieder herunter. Einfach die Methode zu referenzieren bringt nichts, Du musst sie auch *aufrufen*. Und dann könntest Du auch gleich deren Rückgabewert verwenden und damit die Zeile sparen wo Du über den Index -1 auf das letzte Element zugreifst. Das wird von `pop()` ja zurückgegeben.

Da `codeindex` in jedem bis einem Zweig um eins erhöht wird, solltest Du das aus allen Zweigen herausnehmen und hinter die Befehlsauswertung setzen. Der eine Zweig in dem der `codeindex` auf einen Wert gesetzt wird, muss ihn dann halt auf diesen Wert minus eins setzen. Dann funktioniert Dein Interpreter auch wieder mit `code` der andere Zeichen als ausschliesslich BF-Befehle enthält.

Diese ständige ``code[codeindex]``-Wiederholung sollte man vermeiden und den Wert vor der Schleife einmal ermitteln.

Warum eine Schleife die genau am Anfang von `code` bei ']' anders behandelt wird als alle anderen Schleifen verstehe ich nicht. Auch nicht warum die dann *so* behandelt wird.

Anstelle der ``print()``-Anweisungen könntest Du das `logging`-Modul verwenden und `debug()`-Meldungen ausgeben. Die Ausgaben kann man dann einfach unterdrücken wenn man mit der Fehlersuche fertig ist und bei Bedarf auch wieder einschalten.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Jetz hab ich immer wieder list index out of range

Code: Alles auswählen

def bf(code):
    cellindex=0
    cells=[0]*30000
    codeindex=0
    stack_codeindex=[]
    while codeindex <= len(code):
        index=code[codeindex]
        if index==">":
            cellindex +=1
            #print("vorne")
        elif index=="<":
            cellindex -=1
            #print("hinten")
        elif index=="+":
            cells[cellindex] +=1
            #print("inkrementieren")
        elif index=="-":
            cells[cellindex] -=1
            #print("dekrementieren")
        elif index==".":
            print(chr(cells[cellindex]), end="")
            #print("output")

        elif index==",":
            cells[cellindex] = ord(input())
            #print("input")

        elif index=="[":
            stack_codeindex.append(codeindex)
            #print("schleifenanfang")

        elif index=="]":
            if cells[cellindex]!= 0:
                codeindex=stack_codeindex[-1]
                codeindex -=1

            else:
                stack_codeindex.pop
            #print("schleifenende")
            
        codeindex +=1    
                
                
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Den IndexError, dessen Stacktrace Du gerne auch posten darfst, kannst Du doch ganz einfach selbst lösen, indem Du schaust, welcher Index da überläuft und warum.

Backjacks pop-Anmerkung scheinst Du ignoriert zu haben. Brainf*** evaluiert die Bedingung im übrigen am Schleifenanfang und nicht erst am Ende.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

stacktrace

Code: Alles auswählen

bf("""++++++++++
 [
  >+++++++>++++++++++>+++>+<<<<-
 ]                       Schleife zur Vorbereitung der Textausgabe
 >++.                    Ausgabe von 'H'
 >+.                     Ausgabe von 'e'
 +++++++.                'l'
 .                       'l'
 +++.                    'o'
 >++.                    Leerzeichen
 <<+++++++++++++++.      'W'
 >.                      'o'
 +++.                    'r'
 ------.                 'l'
 --------.               'd'
 >+.                     '!'
 >.                      Zeilenvorschub
 +++.                    Wagenrücklauf""")
Hello World!

Traceback (most recent call last):
  File "<pyshell#5>", line 18, in <module>
    +++.                    Wagenrücklauf""")
  File "C:\Python33\z_MyScripts\brainfucker.py", line 7, in bf
    index=code[codeindex]
IndexError: string index out of range
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

könnt ihr mir helfen mit dem Schleifenbedingung am anfang auswerten ich weiss nicht wie?

Code: Alles auswählen

def bf(code):
    cellindex=0
    cells=[0]*30000
    codeindex=0
    stack_codeindex=[]
    while codeindex <= len(code):
        index=code[codeindex]
        if index==">":
            cellindex +=1
            #print("vorne")
        elif index=="<":
            cellindex -=1
            #print("hinten")
        elif index=="+":
            cells[cellindex] +=1
            #print("inkrementieren")
        elif index=="-":
            cells[cellindex] -=1
            #print("dekrementieren")
        elif index==".":
            print(chr(cells[cellindex]), end="")
            #print("output")

        elif index==",":
            cells[cellindex] = ord(input())
            #print("input")

        elif index=="[":
            stack_codeindex.append(codeindex)
            #print("schleifenanfang")

        elif index=="]":
            if cells[cellindex]!= 0:
                codeindex=stack_codeindex[-1]
                codeindex -=1

            else:
                stack_codeindex.pop()
            #print("schleifenende")
            
        codeindex +=1    
                
                
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

Ich tippe mal auf folgendes Problem:
>>> s = "hello"
>>> len(s)
5
>>> s[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range
Nebenbei kann man sich das sparen indem man ein nicht vorkommenes Zeichen am Ende des Code anfügt und eine weiteren Opcode-Auswertungszweig mit Abbruch.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
BlackJack

@fail: Der `IndexError` und die Bemerkung von Sirius3 zum Schleifenanfang haben nichts miteinander zu tun. Zum `IndexError` hat darktrym ja schon etwas gesagt.

Was den Schleifenanfang angeht: Formuliere doch mal präzise in Worten was der BF-Interpreter machen muss, wenn er auf ein '[' trifft. *Das* musst du dann in Code umsetzen wenn ein '[' vorkommt. Statt einen Stapel zu verwenden, könnte man ganz am Anfang auch eine Datenstruktur erstellen wo für jede Klammer der Index der jeweilig zugehörigen anderen Klammer gespeichert wird. Dann braucht man während des Programmablaufs nicht ständig die passende Endklammer suchen, sondern kann direkt darauf zugreifen. Im Zuge dessen könnte man auch gleich die ganzen Zeichen ausfiltern, die keine BF-Befehle sind, damit man sich beim BF-Programmablauf das überlesen dieser Zeichen sparen kann.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Ein fehler weg ein neuer dazu die ASCII ausgaben des Hello Worlds programm stimmen nicht.

Code: Alles auswählen

def bf(code):
    code=[s for s in code if s in ("<",">","+","-",".",",")]
    cellindex=0
    cells=[0]*30000
    codeindex=0
    stack=[]
    while codeindex < len(code):
        index=code[codeindex]
        if index==">":
            cellindex +=1
           
        elif index=="<":
            cellindex -=1
            
        elif index=="+":
            cells[cellindex] +=1
           
        elif index=="-":
            cells[cellindex] -=1
            
        elif index==".":
            print(chr(cells[cellindex]), end="")
         

        elif index==",":
            cells[cellindex] = ord(input())
        

        elif index=="[":
            if cells[cellindex]== 0:
                stack.append(codeindex)
                codeindex=code.index("]",codeindex)
                
        

        elif index=="]":
            codeindex=stack.pop()
            codeindex -=1
            
               
        codeindex +=1    
                
                
BlackJack

@fail: Die Behandlung von '[' ist falsch. Eventuell hast Du den Zweck von dem Stack nicht verstanden und wann der wofür benutzt werden muss. Mach Dir das mal an einem ganz einfachen Beispiel klar. Spiel das in Gedanken durch, sei Python-Interpreter und scheibe Dir für jeden Programmschritt die aktuellen Werte auf ein Blatt Papier.

Ausserdem ist die Suche der passenden schliessenden Klammer falsch. Zum einen der selbe Fehler den ich schon bei der rekursiven Variante ganz am Anfang mal angemerkt hatt: Es wird immer nur nach der *ersten* Klammer im gesamten `code` gesucht. Aber selbst wenn man das anpasst zu „suche die erste schliessende Klammer ab der aktuellen öffnenden Klammer”, ist das immer noch zu simpel gedacht, denn Du musst nicht die *erste*, sondern die *passende* Klammer suchen. Beide Fehler würden bei dem „Hallo Welt”-Programm noch nicht auffallen, aber auch nur weil es nicht mehrere und auch keine verschachtelten Klammern in dem Programm gibt.

Edit: Die Behandlung von ',' entspricht übrigens auch nicht der BF-Definition. Da soll nicht ein Zeichen plus Eingabetaste gelesen werden, sondern wirklich nur ein Bytewert von der Standardeingabe.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Okay jetzt hab ich Probleme erstens wie finde ich die passende Klammer?
und zweitens wie kann ich nur ein zeichen lesen lassen?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Hat zwar nichts mit fails Problemen zu tun, aber hier mal ein bf2py-Converter:

Code: Alles auswählen

cmds={
    '>':'i+=1',
    '<':'i-=1',
    '+':'c[i]+=1',
    '-':'c[i]-=1',
    '.':'sys.stdout.write(chr(c[i]))',
    ',':'c[i]=ord(sys.stdin.read(1))',
    '[':'while c[i]:',
}
brackets= {'[':1, ']':-1}
def bf(code):
    pcode=["c=[0]*100","i=0"]
    ind=0
    for c in code:
        if c in cmds:
            pcode.append(' '*ind+cmds[c])
        ind+=brackets.get(c,0)
    exec '\n'.join(pcode)
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Die erste öffnende Klammer schliesst sich mit der letzten schliessender Klammer.
Die zweite öffnende Klammer schliesst sich mit der zweitletzten schliessender Klammer.

etc.

Oder?
Antworten