Modify piece of code to allow floats and negative numbers as well as an arbitrary amount of spaces in between the characters in the inputted string

I am bad at python :

The following piece of code gets an infix string and will convert it to outfix and output that new expression as a string. However it does not support negative numbers or floats. The following code only allows for single digit values:

Such as (0-9) nothing like 10 or 11. Otherwise it throws a "key error". Also if I add a negative sign it also throws a key error.

class Stack:
    def __init__(self):
        self.items = []

    def isEmpty(self):
        return self.items == []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def peek(self):
        return self.items[len(self.items)-1]

    def size(self):
        return len(self.items)

    def isNumber(self, txt):
        if not isinstance(txt,str) or len(txt.strip())==0:
            print("Argument error in isNumber")
            return False
        # YOUR CODE STARTS HERE
        try:
            float(txt)
            return True
        except ValueError:
            return False

#########################################################################################################

    def infixToPostfix(infixexpr):
        prec = {}
        prec["^"] = 4
        prec["*"] = 3
        prec["/"] = 3
        prec["+"] = 2
        prec["-"] = 2
        prec["("] = 1
        opStack = Stack()
        postfixList = []
        tokenList = infixexpr.split()

        for token in tokenList:
            if token in "0123456789":
                postfixList.append(token)
            elif token == '(':
                opStack.push(token)
            elif token == ')':
                topToken = opStack.pop()
                while topToken != '(':
                    postfixList.append(topToken)
                    topToken = opStack.pop()
            else:
                while (not opStack.isEmpty()) and \
                   (prec[opStack.peek()] >= prec[token]):
                      postfixList.append(opStack.pop())
                opStack.push(token)

        while not opStack.isEmpty():
            postfixList.append(opStack.pop())
        return " ".join(postfixList)

So here is my fix to allow floats as well:

I added this function:

def isNumber(x):
    try:
        float(x)
        return True
    except ValueError:
        return False

And changed this line: if token in "0123456789": into this: if Stack.isNumber(token):

And now the code allows floats.


So what is the other problem? Well the other problem is that my code assumes the input string will have exactly one space in between each of the characters, hence I string.split() to put all the characters in the list. Except the input string can have an arbitrary amount of spaces in between the characters, and if there are no spaces, my code will compare something like "((" to my list of characters and not find it and throw a Key error. So since I have to allow a negative numbers (noted by a minus sign). How can I modify my code so that it will no longer throw the keyerror and allow me to have negative numbers?


When I do this:

print(Stack.infixToPostfix("( ( 1 + 3 ) ) * 4 - ( 9.2 - 0 ) * ( 5 + 8 )"))

My code outputs this: 1 3 + 4 * 9.2 0 - 5 8 + * -

Which works perfectly, however if I remove one space:

"(( 1 + 3 ) ) * 4 - ( 9.2 - 0 ) * ( 5 + 8 )"

My code no longer works. Key error '((' I know why it throws this error (explanation above) but I'm not sure how to fix it.


So final question TL:DR

How to modify my infixtopostfix code to allow for an arbitrary amount of spaces between the characters and allow for negative numbers?

Alex :

First create a separate function which will produce a list of tokens from your string. A token is number (without a sign) or a single character:

def tokenize(s):
    s = re.sub(r"\s+", "", s)
    result = []
    while (s):
        if s[0] in "0123456789":
            number = s[0]
            s = s[1:]
            while (s and s[0] in "0123456789."):
                number += s[0]
                s = s[1:]
            result.append(number)
            if s:
                result.append(s[0])
                s = s[1:]
        else:
            result.append(s[0])
            s = s[1:]
    return result

Then you'll need to keep track of unary plus and minus operations. To do this we introduce a special 'neg' operation - when you process this operation in postfix notation you just negate the value at the top of the operand stack.

You expect unary plus and minus operations at the start of the string or right after the opening '('. After you process a number operand or a closing ')' you reset the unary flag to False, because unary plus or minus cannot appear at these positions. When the unary flag is true you must keep tracking of incoming '+' and '-', use boolean flag 'neg' for it. Change 'neg' state at every '-'. When you finally find an operand - check the state of 'neg' flag. If it's True, then you need to put our special 'neg' operation after the operand. Putting a 'neg' operation after the closing ')' is a bit tricky and requires use of opStack.

def infixToPostfix(infixexpr):
        prec = {}
        prec["^"] = 3
        prec["*"] = 3
        prec["/"] = 3
        prec["+"] = 2
        prec["-"] = 2
        prec["("] = 1
        prec["neg"] = 1
        opStack = Stack()
        postfixList = []
        tokenList = tokenize(infixexpr)
        print(tokenList)

        unary = True
        neg = False

        for token in tokenList:
            if unary and token in "+-":
                if token == '-':
                     neg = not neg
            elif isNumber(token):
                postfixList.append(token)
                if neg:
                    postfixList.append("neg")
                    neg = False
                unary = False
            elif token == '(':
                if neg:
                    opStack.push("neg")
                    neg = False
                opStack.push(token)
                unary = True
            elif token == ')':
                topToken = opStack.pop()
                unary = False
                while topToken != '(':
                    postfixList.append(topToken)
                    topToken = opStack.pop()
                if not opStack.isEmpty() and opStack.peek() == "neg":
                    postfixList.append(opStack.pop())
            else:
                while (not opStack.isEmpty()) and \
                   (prec[opStack.peek()] >= prec[token]):
                      postfixList.append(opStack.pop())
                opStack.push(token)

        while not opStack.isEmpty():
            postfixList.append(opStack.pop())
        return " ".join(postfixList)

Input:

"-(( 1 + 3 ) ) * 4 - ( -9.2 - 0 ) * ( 5 + 8 ) - 4 * (-2)"

Output:

1 3 + neg 4 * 9.2 neg 0 - 5 8 + * - 4 2 neg * -

UPDATE

If you want to process negative numbers as a single negative operand and not like a positive operand followed by a 'neg' operation, then you just need a very small modification of the infixToPostfix method. You only need to modify the elif isNumber(token) branch. I'll put it here complete though:

def infixToPostfix(infixexpr):
        prec = {}
        prec["^"] = 3
        prec["*"] = 3
        prec["/"] = 3
        prec["+"] = 2
        prec["-"] = 2
        prec["("] = 1
        prec["neg"] = 1
        opStack = Stack()
        postfixList = []
        tokenList = tokenize(infixexpr)

        unary = True
        neg = False

        for token in tokenList:
            if unary and token in "+-":
                if token == '-':
                     neg = not neg
            elif isNumber(token):
                if neg:
                    postfixList.append("-" + token)
                else:
                    postfixList.append(token)
                neg = False
                unary = False
            elif token == '(':
                if neg:
                    opStack.push("neg")
                    neg = False
                opStack.push(token)
                unary = True
            elif token == ')':
                topToken = opStack.pop()
                unary = False
                while topToken != '(':
                    postfixList.append(topToken)
                    topToken = opStack.pop()
                if not opStack.isEmpty() and opStack.peek() == "neg":
                    postfixList.append(opStack.pop())
            else:
                while (not opStack.isEmpty()) and \
                   (prec[opStack.peek()] >= prec[token]):
                      postfixList.append(opStack.pop())
                opStack.push(token)

        while not opStack.isEmpty():
            postfixList.append(opStack.pop())
        return " ".join(postfixList)

Now the output is

1 3 + neg 4 * -9.2 0 - 5 8 + * - 4 -2 * -

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=220084&siteId=1