Index not recognized

I’m making a calculator in python and I’ve run into a little problem when processing parentheses. Here’s the code:

def split_par(lst):
    lst_two = lst.copy()
    for i in lst_two:
        if i == '(':
            index = lst_two.index(i)
            del lst_two[0:index + 1]
        elif i == ')':
            close_index = lst_two.index(i)
            del lst_two[close_index:]
            del lst[index:close_index + 1]
    str = pemdas_op(lst_two)
    lst.insert(index, str)
    if '(' in lst:
        split_par(lst)
    return lst

If I run this code with an input of, for example, ['(', 12, '+', 7, ')', '*', '(', 12, '+', 7, ')'], it’ll give me the following error: UnboundLocalError: local variable 'index' referenced before assignment
What I find fascinating is if I change del lst[index:close_index + 1] to del lst[index:close_index + 2] It wont give me an error. It will give me the following output: [19, '*', 19, 7, ')'] Which is obviously not what I need. Also I will detail that the line str = pemdas_op(lst_two) is calling a separate function in my program that runs the list through a stack and consolidates the equations based on the order of operations. I don’t think it’s necessary to see that one but if it is let me know. Anyways, how can I solve this problem?

Don’t remove elements from a list while you’re iterating over it. This changes the output of the iterator and the indexes.

If you call this function with a list where a “)” occurs before a “(”, then you will enter the elif before you enter the if block.

Since the if block hasn’t had a chance to assign a value to index, it throws an error when that value is attempted to be read in the second del line.

Hello, @pattydaone. You can try updating your split_par() code like this:

def split_par(lst):
    lst2 = lst.copy()
    lst3 = lst.copy()
    lst.clear()
    foundPhStart = False 
    textOutsidePh = []
    for i in lst2:
        if i == "(":
            foundPhStart = True
            if textOutsidePh != []:
                pemdasopStr2 =  pemdas_op(textOutsidePh)
                lst.append(pemdasopStr2)
                textOutsidePh.clear()
            del lst3[0:(lst3.index(i)+1)]
        elif foundPhStart == False:
            textOutsidePh.append(i)
        elif i==")":
            foundPhStart = False
            pemdasopStr4 = pemdas_op(lst3[0:lst3.index(i)])
            lst.append(pemdasopStr4)
            del lst3[0:(lst3.index(i)+1)]
    if textOutsidePh != []:
        pemdasopStr3 = pemdas_op(textOutsidePh)
        lst.append(pemdasopStr3)
    return lst

I don’t think I ever thought of it because (as far as I’m concerned at least) there’s not an instance where you’ll put a closing parentheses before an opening one in a mathematical expression, so I just ignored that as a potential issue. However you are right. With further inspection I see that the issue does lie in the elif section of the function. So I changed the code to this to solve the issue:

def split_par(lst):
  if '(' in lst:
    lst_two = lst.copy()
    for i in lst_two:
      if i == '(':
        index = lst_two.index(i)
        index_one = lst.index(i)
        del lst_two[0:index + 1]
      elif i == ')':
        close_index = lst_two.index(i)
        index_two = lst.index(i)
        del lst_two[close_index:]
        del lst[index_one:index_two + 1]
    str = pemdas_op(lst_two)
    lst.insert(index_one, str)
    print(lst)
    split_par(lst)
  else:
    return lst

Unfortunately this code also has a problem, as goes with programing, that (I hope) you can help with. As the code stands, if I input ['(', 12, '+', 7, ')', '*', '(', 12, '+', 7, ')'], it will return none. I honestly don’t know what the issue is because the little print(lst) block at the bottom will print [19, '*', 19] which should then be recursed through the function, where it will fail the initial if statement and go to the else statement where it should return [19, '*', 19], but it doesn’t. It returns None. Clearly I’ve done something wrong in my understanding of what’s going on, and I don’t know what I’ve missed.

The return returns to the most recent caller. Your code is doing something like this (I didn’t follow the logic exactly, but it’s close)…

split_par(<first sequence>)
... # does some stuff
  split_par([19, '*', 19])
  ...
  return [19, '*', 19]
... #ignores the return value from split_par()
returns None

It is possible you want to change the recursive call to

  return split_par(lst)

But since you haven’t included pemdas_op, I can’t run the program.

Here’s the pemdas_op function if you’d like:

def pemdas_op(lst):
  lst_index = 0
  pemdas_stack = ['^', '*', '/', '+', '-']
  while pemdas_stack and len(lst) > 1:
    popped = pemdas_stack.pop(lst_index)
    if popped in lst:
      index = lst.index(popped)
      if lst[index] == '^':
        first = index - 1
        second = index + 1
        amount = lst[first] ** lst[second]
        del lst[first:second+1]
        lst.insert(first, amount)
      elif lst[index] == '*':
        first = index - 1
        second = index + 1
        amount = lst[first] * lst[second]
        del lst[first:second + 1]
        lst.insert(first, amount)
      elif lst[index] == '/':
        first = index - 1
        second = index + 1
        amount = lst[first] / lst[second]
        del lst[first:second + 1]
        lst.insert(first, amount)
      elif lst[index] == '+':
        first = index - 1
        second = index + 1
        amount = lst[first] + lst[second]
        del lst[first:second + 1]
        lst.insert(first, amount)
      elif lst[index] == '-':
        first = index - 1
        second = index + 1
        amount = lst[first] - lst[second]
        del lst[first:second + 1]
        lst.insert(first, amount)
    else:
      continue

    str = 0
    if len(lst) == 1:
      for i in lst:
        str += i
      return str

Although I did change that one line to return split_par(lst) and it worked almost seamlessly, so, thank you.