Is there a way to autogenerate valid arithmetic expressions?
Question: I'm currently trying to create a Python script that will autogenerate space-
delimited arithmetic expressions which are valid. However, I get sample output
that looks like this: `( 32 - 42 / 95 + 24 ( ) ( 53 ) + ) 21`

While the empty parentheses are perfectly OK by me, I can't use this
autogenerated expression in calculations since there's no operator between the
24 and the 53, and the + before the 21 at the end has no second argument.

What I want to know is, is there a way to account for/fix these errors using a
Pythonic solution? (And before anyone points it out, I'll be the first to
acknowledge that the code I posted below is probably the worst code I've
pushed and conforms to...well, very few of Python's core tenets.)

    
    
    import random
    parentheses = ['(',')']
    ops = ['+','-','*','/'] + parentheses
    
    lines = 0
    
    while lines < 1000:
        fname = open('test.txt','a')
        expr = []
        numExpr = lines
        if (numExpr % 2 == 0):
            numExpr += 1
        isDiv = False # Boolean var, makes sure there's no Div by 0
    
        # isNumber, isParentheses, isOp determine whether next element is a number, parentheses, or operator, respectively
        isNumber = random.randint(0,1) == 0 # determines whether to start sequence with number or parentheses
        isParentheses = not isNumber
        isOp = False
        # Counts parentheses to ensure parentheses are matching
        numParentheses = 0
        while (numExpr > 0 or numParentheses > 0):
            if (numExpr < 0 and numParentheses > 0):
                isDiv = False
                expr.append(')')
                numParentheses -= 1
            elif (isOp and numParentheses > 0):
                rand = random.randint(0,5)
                expr.append(ops[rand])
                isDiv = (rand == 3) # True if div op was just appended
                # Checks to see if ')' was appended
                if (rand == 5):
                    isNumber = False
                    isOp = True
                    numParentheses -= 1
                # Checks to see if '(' was appended
                elif (rand == 4):
                    isNumber = True
                    isOp = False
                    numParentheses += 1
                # All other operations go here
                else:
                    isNumber = True
                    isOp = False
            # Didn't add parentheses possibility here in case expression in parentheses somehow reaches 0
            elif (isNumber and isDiv):
                expr.append(str(random.randint(1,100)))
                isDiv = False
                isNumber = False
                isOp = True
            # If a number's up, decides whether to append parentheses or a number
            elif (isNumber):
                rand = random.randint(0,1)
                if (rand == 0):
                    expr.append(str(random.randint(0,100)))
                    isNumber = False
                    isOp = True
                elif (rand == 1):
                    if (numParentheses == 0):
                        expr.append('(')
                        numParentheses += 1
                    else:
                        rand = random.randint(0,1)
                        expr.append(parentheses[rand])
                        if rand == 0:
                            numParentheses += 1
                        else:
                            numParentheses -= 1
                isDiv = False
            numExpr -= 1
    
        fname.write(' '.join(expr) + '\n')
        fname.close()
        lines += 1
    


Answer: Yes, you can generate random arithmetic expressions in a Pythonic way. You
need to change your approach, though. Don't try to generate a string and count
parens. Instead generate a random _expression tree_ , then output that.

By an expression tree, I mean an instance of a class called, say, `Expression`
with subclasses `Number`, `PlusExpression,`MinusExpression`,
'TimesExpression`, `DivideExpression`, and `ParenthesizedExpression`. Each of
these, except `Number` will have fields of type `Expression`. Give each a
suitable `__str__` method. Generate some random expression objects and just
print the "root."

Can you take it from here or would you like me to code it up?

**ADDENDUM** : Some sample starter code. Doesn't generate random expressions
(yet?) but this can be added....

    
    
    # This is just the very beginning of a script that can be used to process
    # arithmetic expressions.  At the moment it just defines a few classes
    # and prints a couple example expressions.
    
    # Possible additions include methods to evaluate expressions and generate
    # some random expressions.
    
    class Expression:
        pass
    
    class Number(Expression):
        def __init__(self, num):
            self.num = num
    
        def __str__(self):
            return str(self.num)
    
    class BinaryExpression(Expression):
        def __init__(self, left, op, right):
            self.left = left
            self.op = op
            self.right = right
    
        def __str__(self):
            return str(self.left) + " " + self.op + " "  + str(self.right)
    
    class ParenthesizedExpression(Expression):
        def __init__(self, exp):
            self.exp = exp
    
        def __str__(self):
            return "(" + str(self.exp) + ")"
    
    e1 = Number(5)
    print e1
    
    e2 = BinaryExpression(Number(8), "+", ParenthesizedExpression(BinaryExpression(Number(7), "*", e1)))
    print e2
    

** ADDENDUM 2 **

Getting back into Python is really fun. I couldn't resist implementing the
random expression generator. It is built on the code above. SORRY ABOUT THE
HARDCODING!!

    
    
    from random import random, randint, choice
    
    def randomExpression(prob):
        p = random()
        if p > prob:
            return Number(randint(1, 100))
        elif randint(0, 1) == 0:
            return ParenthesizedExpression(randomExpression(prob / 1.2))
        else:
            left = randomExpression(prob / 1.2)
            op = choice(["+", "-", "*", "/"])
            right = randomExpression(prob / 1.2)
            return BinaryExpression(left, op, right)
    
    for i in range(10):
        print(randomExpression(1))
    

Here is the output I got:

    
    
    (23)
    86 + 84 + 87 / (96 - 46) / 59
    ((((49)))) + ((46))
    76 + 18 + 4 - (98) - 7 / 15
    (((73)))
    (55) - (54) * 55 + 92 - 13 - ((36))
    (78) - (7 / 56 * 33)
    (81) - 18 * (((8)) * 59 - 14)
    (((89)))
    (59)
    

Ain't tooooo pretty. I think it puts out too many parents. Maybe change the
probability of choosing between parenthesized expressions and binary
expressions might work well....

