为了为表达式解析器生成大型测试数据(基于Dijkstra的调车场),我提出了以下 Python脚本:
#!/usr/bin/python
import ast
import sys
import random
import operator as op
def gen_digit(n):
i = 0
digit = ""
if random.randint(0, 1e06) % 17 == 0:
digit = digit + "-"
while i < n:
if i == 0:
digit = digit + str(random.randint(1, 9))
else:
digit = digit + str(random.randint(0, 9))
i = i + 1
return digit;
def rnd_op():
ops = [ "+", "-", "*", "/", "%" ]
return ops[random.randint(0, 4)]
operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
ast.Div: op.truediv, ast.Mod: op.mod, ast.USub: op.neg}
def eval_(node):
if isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.BinOp):
return operators[type(node.op)](eval_(node.left), eval_(node.right))
elif isinstance(node, ast.UnaryOp):
return operators[type(node.op)](eval_(node.operand))
else:
raise TypeError(node)
def eval_expr(expr):
return eval_(ast.parse(expr, mode='eval').body)
def right_op(op, expr):
if op == "/" or op == "%":
try:
v = eval_expr(expr)
except ZeroDivisionError:
v = 0
if v == 0:
return op + " (" + expr + " + " + gen_digit(random.randint(1, 4)) + ")"
else:
return op + " " + expr
else:
return op + " " + expr
def gen_term():
term = ""
if random.randint(0, 1e06) % 17 == 0:
term += "-"
term += "(" + right_op(gen_digit(random.randint(1, 4)), \
right_op(rnd_op() + " " + gen_digit(random.randint(1, 4)), \
rnd_op() + " " + gen_digit(random.randint(1, 4)))) + ")"
return term
def build_expr():
return "(" + gen_term() + " " + \
right_op(rnd_op(), gen_term()) + " " + \
right_op(rnd_op(), gen_term()) + ")"
def rnd_expr(expr, m, d):
if d < m:
expr = "(" + build_expr() + " " + \
right_op(rnd_op(), rnd_expr(expr, m, d + 1)) + " " + \
right_op(rnd_op(), build_expr()) + ")"
return expr
argc = len(sys.argv)
if argc > 1:
dpth = int(sys.argv[1]);
sys.setrecursionlimit(dpth * 10)
print (rnd_expr(build_expr(), dpth, 0))
else:
print (rnd_expr(build_expr(), 1, 0))
我的分流场实施(另一个C++
项目)是正确的,接受四个基本算术运算符加%(modulo)。
我想让生成的表达式有效,但是目前我偶尔会遇到 division / modulo by zero errors ,尽管我尝试过它们。此外,ast
溢出的递归深度大于98
。
编辑 分区/模数为零错误不在 Python脚本中,而是通过使用{{}等外部工具进行解析1}}在 Linux 上。
有人有一个想法,为什么函数bc
或算法有时会失败。
答案 0 :(得分:2)
实际上 Python脚本正在做它专门做的事情:生成有效的测试数据!
如果你改变了
def rnd_op():
ops = [ "+", "-", "*", "/", "%" ]
return ops[random.randint(0, 4)]
到
def rnd_op():
ops = [ "+", "-", "*", "/", "%" ]
return ops[random.randint(0, 3)]
即。省略模运算符%
的创建,而不是bash
shell中的单行将证明它是正确的:
while(./rnd_expr.py 4 | perl -e 'my $exp = <STDIN>; if(!defined(eval($exp))) { print $@." ".$exp; exit(1) } else { print eval($exp)."\n"; exit(0); }' ); do true; done
当没有更改时,它会抱怨模数为零。使用bc
重新检查会显示相同的结果。
我的主要C++
项目主要接受表达式,而perl
和bc
都主要拒绝它。
所以我的主C++
项目中有一个(可能)优先级错误。
修改: 两者都是正确的,perl/bc
和我的主C++
项目。第一个是将结果解释为integer
并截断中间结果,而我的主C++
项目正在计算符号(即使用分数类)。
另一个证明 rubberduck调试实际上正在运行:)