Writing a Smallf*ck interpreter - python

the problem I'm trying to solve is this Codewars kata: https://www.codewars.com/kata/58678d29dbca9a68d80000d7/train/python
I'm passing 123 out of 124 tests, but the one I'm failing is related to how I'm handling nested loops. I just can't seem to figure out a way to replicate the problem because they don't show you the test input. Any help is appreciated; here's what I have for the problem so far:
import re
def interpreter(code, tape):
#Filter out non-command characters
code_pure = re.findall(r"(\>|\<|\*|\[|\])", code)
sf = "".join(code_pure)
#Convert tape to list so that elements are mutable
tape_list = []
for bit in tape:
tape_list.append(bit)
#Keep track of pointer and instruction position
pointer = 0
instr = 0
brack_pos = [] #Contains positions of "[" brackets
while instr < len(sf):
#If pointer goes out of bounds then end the program
if pointer >= len(tape_list) or pointer < 0:
return "".join(tape_list)
#"*" flips the current bit
if sf[instr] == "*":
if tape_list[pointer] == "1":
tape_list[pointer] = "0"
elif tape_list[pointer] == "0":
tape_list[pointer] = "1"
instr += 1
#Move right one bit
elif sf[instr] == ">":
pointer += 1
instr += 1
#Move left one bit
elif sf[instr] == "<":
pointer -= 1
instr += 1
elif sf[instr] == "[":
#If pointer is on 0, skip the loop
if tape_list[pointer] == "0":
brack_cnt = 1
while brack_cnt != 0:
instr += 1
if sf[instr] == "[":
brack_cnt += 1
elif sf[instr] == "]":
brack_cnt -= 1
instr += 1
#If pointer is 1, step into the loop
elif tape_list[pointer] != "0":
brack_pos.append(instr)
instr += 1
elif sf[instr] == "]":
if tape_list[pointer] == "0":
instr += 1
elif tape_list[pointer] != "0":
instr = brack_pos[-1]
brack_pos.pop()
return "".join(tape_list)

The problem is in the last if-block checking for sf[instr] == "]" where brack_pos.pop() is executed only when looping back to the opening bracket, not when moving past the closing bracket. I.e. brack_pos.pop() needs to be unconditional.
Seems like a fun exercise btw., here's what I came up with:
def interpreter(code, tape):
# Implement your interpreter here
tape = list(tape)
# Find matching brackets
jump = [0]*len(code)
open_bracket_stack = []
for i,c in enumerate(code):
if c == '[':
open_bracket_stack.append(i)
elif c == ']':
matching = open_bracket_stack.pop()
jump[i] = matching
jump[matching] = i
code_ptr = 0
tape_ptr = 0
while(code_ptr < len(code) and tape_ptr < len(tape) and tape_ptr >= 0):
c = code[code_ptr]
if c == '>':
tape_ptr += 1
elif c == '<':
tape_ptr -= 1
elif c == '*':
tape[tape_ptr] = '1' if tape[tape_ptr] == '0' else '0'
elif c == '[' and tape[tape_ptr] == '0':
code_ptr = jump[code_ptr]
elif c == ']' and tape[tape_ptr] == '1':
code_ptr = jump[code_ptr]
code_ptr += 1
return "".join(tape)

Related

TypeError: list indices must be integers or slices, not str, Python [duplicate]

This question already has answers here:
Iterating each character in a string using Python
(9 answers)
Closed 1 year ago.
I need to display the number of vowels in the entered text, if they are in the text
TypeError: list indices must be integers or slices, not str
a = 0
e = 0
y = 0
u = 0
o = 0
i = 0
word = input("Введите текст: ")
word1 = list(word)
print(word1)
for j in word1:
if word1[j] == "a":
a += 1
elif word1[j] == "e":
e += 1
elif word1[j] == "y":
y += 1
elif word1[j] == "u":
u += 1
elif word1[j] == "o":
o += 1
elif word1[j] == "i":
i += 1
if a != 0:
print("a:", a)
if e != 0:
print("e:", e)
if y != 0:
print("y:", y)
if u != 0:
print("u:", u)
if o != 0:
print("o:", o)
if i != 0:
print("i:", i)
When you iterate over a string it returns the characters and not the positions.
For example:
for _c in 'test':
print(_c)
will result in:
t
e
s
t
You should change it to:
for j, _c in enumerate(word1):
...
What enumerate does is adding a counter to the element you are iterating on, you can find more info here

List has no items to pop however there was something there?

Trying to make a brainfuck interpreter in python from scratch just for fun, I'm almost done with the code however this small error is coming up whenever I try interpreting ",[>++++<]" Which is supposed to input a number, and multiply it by 4. It stops and gets this error
File ".\bfInterpreter.py", line 42, in endWhile
if self.stack[self.pointer]: self.place = self.inWhile.pop(-1)
IndexError: pop from empty list
Any ideas?
here is the code that the error is in:
import sys
import time
class Operator:
def __init__(self) -> None:
self.pointer = 0
self.place = 0
self.inWhile = []
self.stack = [0]
def plus(self):
self.stack[self.pointer]+=1
def minus(self):
self.stack[self.pointer]-=1
def left(self):
if self.pointer > 0: self.pointer -= 1
else: raise IndexError
def right(self):
self.pointer += 1
try: self.stack[self.pointer] = 0
except: self.stack.append(0)
def output(self):
print(ascii(self.stack[self.pointer]))
def inp(self):
val = input("Num: ")
try: self.stack[self.pointer] = int(val)
except:
print("Please input a number")
self.inp()
def startWhile(self):
self.inWhile.append(self.place)
print(self.inWhile)
def endWhile(self):
print(self.inWhile)
if self.stack[self.pointer]: self.place = self.inWhile.pop(-1)
def interpret(self, bf):
self.place = 0
while self.place < len(bf):
if bf[self.place] == "+": self.plus()
elif bf[self.place] == "-": self.minus()
elif bf[self.place] == "<": self.left()
elif bf[self.place] == ">": self.right()
elif bf[self.place] == "[": self.startWhile(bf)
elif bf[self.place] == "]": self.endWhile()
elif bf[self.place] == ",": self.inp()
elif bf[self.place] == ".": self.output()
else: raise SyntaxError
self.place += 1
print(self.stack)
def main():
start = time.time()
op = Operator()
op.interpret(",[>++++<]")
print(f"Ended in {time.time() - start} second(s)")
if __name__ == "__main__": main()
Edit: self.stack is basically the memory that brainfuck is allowed to access and edit, so the "[" starts a while loop, checking if a certain spot in the stack has reached 0 every cycle, and if it's not 0, it repeats the code in the brackets
The main problem is self.inWhile.pop(-1) in command ] - it removes value from list and in next loop this list is empty - so pop(-1) raises your error message.
But there is more mistakes and problems in [ and ].
All commands runs self.place += 1 but ] shouldn't do this because this way it jump to first command after [ but it should run again [. Maybe if it would run again [ then it could add again value to inWhile and self.inWhile.pop(-1) whouldn't make problem.
But there is other problem with [ - it should check if value is 0 and jump to first command after ]. It would need another list to remember position of ] but it seems more complicated - because if at start value is 0 then it couldn't yet run ] and it couldn't add position to list. Better is to search matching ] in string with commands. The same method I used to search [ (instead of using inWhile).
Other problem is > you always try to set stack[place] = 0 but if you run > and next < and again > then stack[place] may already exists and it may have some value and stack[place] = 0 may remove this value. I rather use while loop with pointer >= len(stack) to append values only if stack is really too short.
Other problem is that program in branfuck runs loop forever because it doesn't have - to decreate inputed value. It needs also >. after loop to display result.
,[->++++<]>.
My version without inWhile but with loop which search matching [ or matching ].
import sys
import time
class Operator:
def __init__(self):
self.place = 0 # command place
self.pointer = 0 # stack pointer
self.stack = [0]
def input(self):
#print('[DEBUG] input')
while True:
val = input("Number: ")
try:
self.stack[self.pointer] = int(val)
break
except:
print("Please input a integer number")
def output(self):
#print('[DEBUG] output')
print(ascii(self.stack[self.pointer]))
def plus(self):
#print('[DEBUG] plus')
self.stack[self.pointer] += 1
def minus(self):
#print('[DEBUG] minus')
self.stack[self.pointer] -= 1
def left(self):
#print('[DEBUG] left')
if self.pointer > 0:
self.pointer -= 1
else:
raise IndexError
def right(self):
#print('[DEBUG] right')
self.pointer += 1
while len(self.stack) <= self.pointer:
self.stack.append(0)
def start_loop(self):
#print('[DEBUG] start_loop')
#print('self.place:', self.place)
if self.stack[self.pointer] == 0:
# find matching `]` to jump to next command after `]`
counter = 1
while counter > 0:
self.place += 1
char = self.bf[self.place]
if char == '[':
counter += 1
elif char == ']':
counter -= 1
#print('counter:', counter)
def end_loop(self):
#print('[DEBUG] end_loop')
#print('self.place:', self.place)
# find matching `[` to run again `[` and all loop
counter = 1
while counter > 0:
self.place -= 1
char = self.bf[self.place]
if char == '[':
counter -= 1
elif char == ']':
counter += 1
#print('counter:', counter)
def interpret(self, bf):
self.bf = bf
self.place = 0
while self.place < len(bf):
char = self.bf[self.place]
#print('[DEBUG] interpret:', self.place, bf[self.place])
if char == ",":
self.input()
self.place += 1
elif char == ".":
self.output()
self.place += 1
elif char == "+":
self.plus()
self.place += 1
elif char == "-":
self.minus()
self.place += 1
elif char == "<":
self.left()
self.place += 1
elif char == ">":
self.right()
self.place += 1
elif char == "[":
self.start_loop()
self.place += 1
elif char == "]":
self.end_loop()
# it has to be without `self.place += 1`
else:
raise SyntaxError
print('stack:', self.stack)
def main():
start = time.time()
op = Operator()
op.interpret(",[->++++<]>.") # input*4
#op.interpret(",[->+>++>+++>++++<<<<]>.>.>.>.") # input*1, input*2, input*3, input*4
#op.interpret(",[->+<>>++<<>>>+++<<<>>>>++++<<<<]>.>.>.>.") # input*1, input*2, input*3, input*4
end = time.time()
print(f"Ended in {end - start:.2f} second(s)")
if __name__ == "__main__":
main()

brainfuck intepreter printing out wrong

I recently decided to try to code "yet another" brainfuck interpreter, but I have a problem. It prints out the wrong numbers, when it should put our hello,world!. Does anyone know why this is happening? Thanks in advance! I cannot figure out why it's doing this. Please help! I am only a beginner. Sorry for the bad coding style, but please don't change the whole thing. I would rather recieve tips.
from copy import copy
import sys
def brainfuckintepreter(program):
# find pairs of brackets
brackets = {}
bbrackets = {}
def findmatchingclosingbracket(ctr):
level = 0
if program[ctr] == '[':
while True:
if program[ctr] == '[':
level += 1
elif program[ctr] == ']':
level -= 1
if level == 0:
return ctr
break
ctr += 1
def findmatchingopeningbracket(ctr):
level = 0
if program[ctr] == ']':
while True:
if program[ctr] == '[':
level -= 1
elif program[ctr] == ']':
level += 1
if level == 0:
return ctr
break
ctr -= 1
"""
ctr = 0
for a in program:
if a == '[':
f = copy(findmatchingclosingbracket(ctr))
brackets[ctr] = f
bbrackets[f] = ctr
ctr += 1
print(brackets)
print(bbrackets)
"""
# running the program
tape = [0] * 3000
pointer = 1500
counter = 0
results = ""
valid = True
while counter != len(program) and valid:
a = program[counter]
# move right
if a == '>':
if pointer == len(tape) - 1:
tape.append(0)
pointer += 1
# move left
elif a == '<':
if pointer == 0:
raise ValueError("On index ", counter, ", the program tried to move to -1 on the tape")
valid = False
return valid
else:
pointer -= 1
# increment
elif a == '+':
if tape[pointer] == 255:
tape[pointer] = 0
else:
tape[pointer] += 1
# decrement
elif a == '-':
if tape[pointer] == 0:
tape[pointer] = 255
else:
tape[pointer] -= 1
# output character
elif a == '.':
t = chr(tape[pointer])
results += t
print(t, end='')
# input character
elif a == ',':
tape[pointer] = ord(sys.stdin.read(1))
# opening bracket
elif a == '[':
if tape[pointer] == 0:
pointer = findmatchingclosingbracket(pointer)
# closing bracket
elif a == ']':
if tape[pointer] != 0:
pointer = findmatchingopeningbracket(counter)
counter += 1
"""
for b in tape:
if b != 0:
print(b)
"""
brainfuckintepreter('++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.')
Edit:
I changed my code after some revisions, but same problem.
Your loops are the problem.
level = 0
while level > 0:
...
That loop will never be entered. The condition is immediately false (you're setting level to 0, right before checking if it is greater than 0).
You could change that to a do..while loop instead of a while loop (make a while true loop and check the condition at the end to decide whether to break out of the loop or not) and start checking at the current pointer position (include the current [ or ]), not at the next character after it.
Also here:
if tape[pointer] == 0:
pointer = findmatchingclosingbracket(pointer)
you should be passing and updating the program counter, not the tape pointer on the second line. And likewise for the other one just below it.

Iterating through list (Python)

I have a list that extends on like this and I would like to sort them based on the first 2 digits of the second part of each. I'm really rushed right now so some help would be nice.
collection = ["81851 19AJA01", "68158 17ARB03", "104837 20AAH02",
I tried this and it didn't work. I'm not doing this for a class I'd really appropriate some help
for x in collection:
counter = 0
i=0
for y in len(str(x)):
if (x[i] == '1'):
counter == 1
elif (x[i] == '2'):
counter == 2
elif x[i] == '0' and counter == 2:
counter = 2
elif x[i] == '9' and counter == 1:
counter = 3
elif x[i] == '8' and counter == 1:
counter = 4
elif x[i] == '7' and counter == 1:
counter = 5
i = i + 1
if (counter==2):
freshmen.append(x)
elif (counter==3):
sophomores.append(x)
elif (counter==4):
juniors.append(x)
elif (counter==5):
seniors.append(x)
Use the key function to define a custom sorting rule:
In [1]: collection = ["81851 19AJA01", "68158 17ARB03", "104837 20AAH02"]
In [2]: sorted(collection, key=lambda x: int(x.split()[1][:2]))
Out[2]: ['68158 17ARB03', '81851 19AJA01', '104837 20AAH02']

Python Recursion Double letters

So what the task is, is that your supposed to write a recursion function that counts the amount of "double" letters in a string, So for example the string "hmmm" would return 1 and the string "hmmmm" would return 2 and that a string "abb" would return 1. My code is here:
def num_double_letters(astr):
if astr == "" or len(astr) == 1:
return 0
elif len(astr) == 2:
if astr[0] == astr[1]:
return 1 + num_double_letters(astr[1:])
else:
return 0 + num_double_letters(astr[1:])
elif astr[0] != astr[1]:
return 0 + num_double_letters(astr[1:])
elif astr[0] == astr[1] != astr[2]:
return 1 + num_double_letters(astr[1:])
elif astr[0] == astr[1] == astr[2]:
return 0 + num_double_letters(astr[1:])
My problem is that a string with 4 same letters = 1 when its supposed to = 2. And also is there a cleaner way to do this?
I think you've made it a bit complicated for yourself... there's no need to go deeper into the recursion once the length of your string is 2, and you want to advance by 2, not 1 when you find a double to count the way I think you do. Try this:
def num_double_letters(astr):
if astr == "" or len(astr) == 1:
return 0
elif len(astr) == 2:
if astr[0] == astr[1]:
return 1
else:
return 0
elif astr[0] != astr[1]:
return 0 + num_double_letters(astr[1:])
elif astr[0] == astr[1]:
return 1 + num_double_letters(astr[2:])
print(num_double_letters('hmm'))
print(num_double_letters('hmmm'))
print(num_double_letters('hmmmm'))
Output:
1
1
2
You might consider the following more Pythonic and concise:
def num_double_letters(astr):
if len(astr) < 2:
return 0
if astr[0] == astr[1]:
return 1 + num_double_letters(astr[2:])
return num_double_letters(astr[1:])

Categories

Resources