Python 有没有办法自动生成有效的算术表达式?
我目前正在尝试创建一个Python脚本,该脚本将自动生成有效的以空格分隔的算术表达式。但是,我得到的示例输出如下所示:Python 有没有办法自动生成有效的算术表达式?,python,parsing,Python,Parsing,我目前正在尝试创建一个Python脚本,该脚本将自动生成有效的以空格分隔的算术表达式。但是,我得到的示例输出如下所示:(32-42/95+24()(53)+)21 虽然空括号对我来说很好,但我不能在计算中使用这个自动生成的表达式,因为24和53之间没有运算符,并且结尾处21之前的+没有第二个参数 我想知道的是,有没有办法使用Pythonic解决方案来解释/修复这些错误?(在任何人指出这一点之前,我将首先承认,我在下面发布的代码可能是我推过的最糟糕的代码,并且符合……好吧,很少有Python的核心
(32-42/95+24()(53)+)21
虽然空括号对我来说很好,但我不能在计算中使用这个自动生成的表达式,因为24和53之间没有运算符,并且结尾处21之前的+没有第二个参数
我想知道的是,有没有办法使用Pythonic解决方案来解释/修复这些错误?(在任何人指出这一点之前,我将首先承认,我在下面发布的代码可能是我推过的最糟糕的代码,并且符合……好吧,很少有Python的核心原则。)
随机导入
括号=['(',')']
ops=['+'、'-'、'*'、'/']+括号
直线=0
当线路小于1000时:
fname=open('test.txt','a')
expr=[]
numExpr=行
如果(numExpr%2==0):
numExpr+=1
isDiv=False#布尔变量,确保0没有Div
#isNumber、IsParenthes、isOp分别确定下一个元素是数字、括号还是运算符
isNumber=random.randint(0,1)=0#确定序列是以数字开始还是以括号开始
isParenthess=不是isNumber
isOp=假
#对括号进行计数以确保括号匹配
numParentheses=0
而(numExpr>0或numParentheses>0):
如果(numExpr<0且numParentheses>0):
isDiv=假
expr.append(')')
numParentheses-=1
elif(等值线图和numParentheses>0):
rand=random.randint(0,5)
expr.append(操作[rand])
isDiv=(rand==3)#如果刚刚附加了div op,则为True
#检查是否附加了“)”
如果(兰德==5):
isNumber=False
isOp=真
numParentheses-=1
#检查是否附加了“(”
elif(兰特=4):
isNumber=True
isOp=假
numParentheses+=1
#所有其他行动都在这里进行
其他:
isNumber=True
isOp=假
#未在此处添加括号,以防括号中的表达式以某种方式达到0
elif(isNumber和isDiv):
expr.append(str(random.randint(1100)))
isDiv=假
isNumber=False
isOp=真
#如果有一个数字,则决定是追加括号还是追加数字
elif(isNumber):
rand=random.randint(0,1)
如果(rand==0):
expr.append(str(random.randint(0100)))
isNumber=False
isOp=真
elif(兰德==1):
如果(numParentheses==0):
expr.append(“(”)
numParentheses+=1
其他:
rand=random.randint(0,1)
expr.append(括号[rand])
如果rand==0:
numParentheses+=1
其他:
numParentheses-=1
isDiv=假
numExpr-=1
fname.write(“”.join(expr)+'\n')
fname.close()
行数+=1
是的,您可以以Python方式生成随机算术表达式。不过,您需要更改方法。不要尝试生成字符串并计算参数。而是生成随机表达式树,然后输出该树
通过表达式树,我指的是一个名为expression
的类的实例,该类具有子类Number
、PlusExpression、MinusExpression、'timeexpression
、DivideExpression
和圆括号表达式
。除Number
外,每一个类都有类型的字段ode>Expression
。为每个对象指定一个合适的\uuuu str\uuuu
方法。生成一些随机表达式对象,然后只打印“根”
你能从这里拿走它吗?还是要我把它编出来
附录:一些示例启动程序代码。不会生成随机表达式(尚未生成?),但可以添加
# 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
**增编2**
回到Python真的很有趣。我忍不住实现了随机表达式生成器。它是基于上面的代码构建的。很抱歉硬编码
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))
以下是我得到的输出:
(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)
不太漂亮。我认为它会让太多的父母失望。也许改变在括号表达式和二进制表达式之间选择的概率可能会很好…事实上,只要Ray Toal的回答在形式上是正确的,对于这样一个简单的问题,你不必对每个操作符都进行子类化非常有效的ode:
import random
import math
class Expression(object):
OPS = ['+', '-', '*', '/']
GROUP_PROB = 0.3
MIN_NUM, MAX_NUM = 0, 20
def __init__(self, maxNumbers, _maxdepth=None, _depth=0):
"""
maxNumbers has to be a power of 2
"""
if _maxdepth is None:
_maxdepth = math.log(maxNumbers, 2) - 1
if _depth < _maxdepth and random.randint(0, _maxdepth) > _depth:
self.left = Expression(maxNumbers, _maxdepth, _depth + 1)
else:
self.left = random.randint(Expression.MIN_NUM, Expression.MAX_NUM)
if _depth < _maxdepth and random.randint(0, _maxdepth) > _depth:
self.right = Expression(maxNumbers, _maxdepth, _depth + 1)
else:
self.right = random.randint(Expression.MIN_NUM, Expression.MAX_NUM)
self.grouped = random.random() < Expression.GROUP_PROB
self.operator = random.choice(Expression.OPS)
def __str__(self):
s = '{0!s} {1} {2!s}'.format(self.left, self.operator, self.right)
if self.grouped:
return '({0})'.format(s)
else:
return s
for i in range(10):
print Expression(4)
好的,我忍不住添加了我自己的实现,使用了我们在Ray的回答中讨论的一些想法。我处理一些事情与Ray不同
我添加了一些关于每个操作符发生概率的处理。操作符是有偏差的,因此低优先级操作符(更大的优先级值)比高阶操作符更常见
我也只在需要优先级时才实现了括号。由于整数具有最高优先级(最低优先级值),它们永远不会被包装在括号中。表达式树中不需要括号表达式作为节点
使用操作符的概率偏向初始水平(使用二次函数)以获得更好的操作符分布。选择不同的指数可以更好地控制输出的质量,但我没有太多地考虑这些可能性
为了好玩,我进一步实现了一个计算器,并过滤掉了不确定的表达式
import sys
import random
# dictionary of operator precedence and incidence probability, with an
# evaluator added just for fun.
operators = {
'^': {'prec': 10, 'prob': .1, 'eval': lambda a, b: pow(a, b)},
'*': {'prec': 20, 'prob': .2, 'eval': lambda a, b: a*b},
'/': {'prec': 20, 'prob': .2, 'eval': lambda a, b: a/b},
'+': {'prec': 30, 'prob': .25, 'eval': lambda a, b: a+b},
'-': {'prec': 30, 'prob': .25, 'eval': lambda a, b: a-b}}
max_levels = 3
integer_range = (-100, 100)
random.seed()
# A node in an expression tree
class expression(object):
def __init__(self):
super(expression, self).__init__()
def precedence(self):
return -1
def eval(self):
return 0
@classmethod
def create_random(cls, level):
if level == 0:
is_op = True
elif level == max_levels:
is_op = False
else:
is_op = random.random() <= 1.0 - pow(level/max_levels, 2.0)
if is_op:
return binary_expression.create_random(level)
else:
return integer_expression.create_random(level)
class integer_expression(expression):
def __init__(self, value):
super(integer_expression, self).__init__()
self.value = value
def __str__(self):
return self.value.__str__()
def precedence(self):
return 0
def eval(self):
return self.value
@classmethod
def create_random(cls, level):
return integer_expression(random.randint(integer_range[0],
integer_range[1]))
class binary_expression(expression):
def __init__(self, symbol, left_expression, right_expression):
super(binary_expression, self).__init__()
self.symbol = symbol
self.left = left_expression
self.right = right_expression
def eval(self):
f = operators[self.symbol]['eval']
return f(self.left.eval(), self.right.eval())
@classmethod
def create_random(cls, level):
symbol = None
# Choose an operator based on its probability distribution
r = random.random()
cumulative = 0.0
for k, v in operators.items():
cumulative += v['prob']
if r <= cumulative:
symbol = k
break
assert symbol != None
left = expression.create_random(level + 1)
right = expression.create_random(level + 1)
return binary_expression(symbol, left, right)
def precedence(self):
return operators[self.symbol]['prec']
def __str__(self):
left_str = self.left.__str__()
right_str = self.right.__str__()
op_str = self.symbol
# Use precedence to determine if we need to put the sub expressions in
# parentheses
if self.left.precedence() > self.precedence():
left_str = '('+left_str+')'
if self.right.precedence() > self.precedence():
right_str = '('+right_str+')'
# Nice to have space around low precedence operators
if operators[self.symbol]['prec'] >= 30:
op_str = ' ' + op_str + ' '
return left_str + op_str + right_str
max_result = pow(10, 10)
for i in range(10):
expr = expression.create_random(0)
try:
value = float(expr.eval())
except:
value = 'indeterminate'
print expr, '=', value
有一家公司
(5 * 12 / 16)
6 * 3 + 14 + 0
13 + 15 - 1
19 + (8 / 8)
(12 + 3 - 5)
(4 * 0 / 4)
1 - 18 / (3 * 15)
(3 * 16 + 3 * 1)
(6 + 16) / 16
(8 * 10)
import sys
import random
# dictionary of operator precedence and incidence probability, with an
# evaluator added just for fun.
operators = {
'^': {'prec': 10, 'prob': .1, 'eval': lambda a, b: pow(a, b)},
'*': {'prec': 20, 'prob': .2, 'eval': lambda a, b: a*b},
'/': {'prec': 20, 'prob': .2, 'eval': lambda a, b: a/b},
'+': {'prec': 30, 'prob': .25, 'eval': lambda a, b: a+b},
'-': {'prec': 30, 'prob': .25, 'eval': lambda a, b: a-b}}
max_levels = 3
integer_range = (-100, 100)
random.seed()
# A node in an expression tree
class expression(object):
def __init__(self):
super(expression, self).__init__()
def precedence(self):
return -1
def eval(self):
return 0
@classmethod
def create_random(cls, level):
if level == 0:
is_op = True
elif level == max_levels:
is_op = False
else:
is_op = random.random() <= 1.0 - pow(level/max_levels, 2.0)
if is_op:
return binary_expression.create_random(level)
else:
return integer_expression.create_random(level)
class integer_expression(expression):
def __init__(self, value):
super(integer_expression, self).__init__()
self.value = value
def __str__(self):
return self.value.__str__()
def precedence(self):
return 0
def eval(self):
return self.value
@classmethod
def create_random(cls, level):
return integer_expression(random.randint(integer_range[0],
integer_range[1]))
class binary_expression(expression):
def __init__(self, symbol, left_expression, right_expression):
super(binary_expression, self).__init__()
self.symbol = symbol
self.left = left_expression
self.right = right_expression
def eval(self):
f = operators[self.symbol]['eval']
return f(self.left.eval(), self.right.eval())
@classmethod
def create_random(cls, level):
symbol = None
# Choose an operator based on its probability distribution
r = random.random()
cumulative = 0.0
for k, v in operators.items():
cumulative += v['prob']
if r <= cumulative:
symbol = k
break
assert symbol != None
left = expression.create_random(level + 1)
right = expression.create_random(level + 1)
return binary_expression(symbol, left, right)
def precedence(self):
return operators[self.symbol]['prec']
def __str__(self):
left_str = self.left.__str__()
right_str = self.right.__str__()
op_str = self.symbol
# Use precedence to determine if we need to put the sub expressions in
# parentheses
if self.left.precedence() > self.precedence():
left_str = '('+left_str+')'
if self.right.precedence() > self.precedence():
right_str = '('+right_str+')'
# Nice to have space around low precedence operators
if operators[self.symbol]['prec'] >= 30:
op_str = ' ' + op_str + ' '
return left_str + op_str + right_str
max_result = pow(10, 10)
for i in range(10):
expr = expression.create_random(0)
try:
value = float(expr.eval())
except:
value = 'indeterminate'
print expr, '=', value
(4 + 100)*41/46 - 31 - 18 - 2^-83 = -13.0
(43 - -77)/37^-94 + (-66*67)^(-24*49) = 3.09131533541e+149
-32 - -1 + 74 + 74 - 15 + 64 - -22/98 = 37.0
(-91*-4*45*-55)^(-9^2/(82 - -53)) = 1.0
-72*-85*(75 - 65) + -100*19/48*22 = 61198.0
-57 - -76 - -54*76 + -38 - -23 + -17 - 3 = 4088.0
(84*-19)^(13 - 87) - -10*-84*(-28 + -49) = 64680.0
-69 - -8 - -81^-51 + (53 + 80)^(99 - 48) = 2.07220963807e+108
(-42*-45)^(12/87) - -98 + -23 + -67 - -37 = 152.0
-31/-2*-58^-60 - 33 - -49 - 46/12 = -79.0
import random
def expr(depth):
if depth==1 or random.random()<1.0/(2**depth-1):
return str(int(random.random() * 100))
return '(' + expr(depth-1) + random.choice(['+','-','*','/']) + expr(depth-1) + ')'
for i in range(10):
print expr(4)
from random import random, choice
UNARIES = ["sqrt(%s)", "exp(%s)", "log(%s)", "sin(%s)", "cos(%s)", "tan(%s)",
"sinh(%s)", "cosh(%s)", "tanh(%s)", "asin(%s)", "acos(%s)",
"atan(%s)", "-%s"]
BINARIES = ["%s + %s", "%s - %s", "%s * %s", "%s / %s", "%s ** %s"]
PROP_PARANTHESIS = 0.3
PROP_BINARY = 0.7
def generate_expressions(scope, num_exp, num_ops):
scope = list(scope) # make a copy first, append as we go
for _ in xrange(num_ops):
if random() < PROP_BINARY: # decide unary or binary operator
ex = choice(BINARIES) % (choice(scope), choice(scope))
if random() < PROP_PARANTHESIS:
ex = "(%s)" % ex
scope.append(ex)
else:
scope.append(choice(UNARIES) % choice(scope))
return scope[-num_exp:] # return most recent expressions
scope = [c for c in "abcde"]
for expression in generate_expressions(scope, 10, 50):
print expression
e / acos(tan(a)) / a * acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a)
(a + (a ** sqrt(e)))
acos((b / acos(tan(a)) / a + d) / (a ** sqrt(e)) * (a ** sinh(b) / b))
sin(atan(acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a)))
sin((b / acos(tan(a)) / a + d)) / (a ** sinh(b) / b)
exp(acos(tan(a)) / a + acos(e))
tan((b / acos(tan(a)) / a + d))
acos(tan(a)) / a * acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a) + cos(sqrt(e))
(acos(tan(a)) / a + acos(e) * a + e)
((b / acos(tan(a)) / a + d) - cos(sqrt(e))) + sinh(b)
scope = range(100)
43 * (50 * 83)
34 / (29 / 24)
66 / 47 - 52
((88 ** 38) ** 40)
34 / (29 / 24) - 27
(16 + 36 ** 29)
55 ** 95
70 + 28
6 * 32
(52 * 2 ** 37)