使用解析将 ** 运算符更改为电源函数?
changing ** operator to power function using parsing?
我的要求是将**运算符改为幂函数
例如
1.Input -"B**2"
Output - power(B,2)
2."B**2&&T**2*X"
Output - power(B,2)
我写了下面的正则表达式来解决这个问题
rx=r"([a-zA-Z0-9]+)\*\*([a-zA-Z0-9()]+)"
result = regex.sub(rx, r"power(,)", expression, 0, regex.IGNORECASE | regex.MULTILINE)
但是上面的代码成功转换了类似于示例1和示例2的表达式,但是没有转换成(a+1)**2 or ((a+b)*c)**2
这样的表达式。我意识到正则表达式不是处理此类情况的最佳方式。而不是解析将是处理该问题的最佳方法。
我对 python 有点陌生。请指导我如何解决这个问题。
如果您需要处理一般的 Python 语法,请查看 ast
模块。您可能想要 ast.parse
the string into an abstract syntax tree and use an ast.NodeTransformer
来替换节点。但是,没有 ast.unparse
,因此您需要使用第三方依赖项或自己编写解析处理。
如果你只需要处理算术运算(特别是如果你想 拒绝 其他 Python 语法),你可能想用一些东西编写你自己的语法像 pyparsing
.
这听起来很熟悉,我想我在 pyparsing 邮件列表上处理过类似的问题,但我暂时找不到。但是尝试这样的事情:
from pyparsing import *
# define some basic operand expressions
number = Regex(r'\d+(\.\d*)?([Ee][+-]?\d+)?')
ident = Word(alphas+'_', alphanums+'_')
# forward declare our overall expression, since a slice could
# contain an arithmetic expression
expr = Forward()
slice_ref = '[' + expr + ']'
# define our arithmetic operand
operand = number | Combine(ident + Optional(slice_ref))
# parse actions to convert parsed items
def convert_to_pow(tokens):
tmp = tokens[0][:]
ret = tmp.pop(-1)
tmp.pop(-1)
while tmp:
base = tmp.pop(-1)
# hack to handle '**' precedence ahead of '-'
if base.startswith('-'):
ret = '-pow(%s,%s)' % (base[1:], ret)
else:
ret = 'pow(%s,%s)' % (base, ret)
if tmp:
tmp.pop(-1)
return ret
def unary_as_is(tokens):
return '(%s)' % ''.join(tokens[0])
def as_is(tokens):
return '%s' % ''.join(tokens[0])
# simplest infixNotation - may need to add a few more operators, but start with this for now
arith_expr = infixNotation( operand,
[
('-', 1, opAssoc.RIGHT, as_is),
('**', 2, opAssoc.LEFT, convert_to_pow),
('-', 1, opAssoc.RIGHT, unary_as_is),
(oneOf("* /"), 2, opAssoc.LEFT, as_is),
(oneOf("+ -"), 2, opAssoc.LEFT, as_is),
])
# now assign into forward-declared expr
expr <<= arith_expr.setParseAction(lambda t: '(%s)' % ''.join(t))
assert "2**3" == expr
assert "2**-3" == expr
# test it out
tests = [
"2**3",
"2**-3",
"2**3**x5",
"2**-3**x6[-1]",
"2**-3**x5+1",
"(a+1)**2",
"((a+b)*c)**2",
"B**2",
"-B**2",
"(-B)**2",
"B**-2",
"B**(-2)",
"B**2&&T**2*X",
]
x5 = 2
a,b,c = 1,2,3
B = 4
x6 = [3,2]
for test in tests:
print test
xform = expr.transformString(test)[1:-1]
print xform
print '**' not in xform and eval(xform) == eval(test)
print
打印:
2**3
pow(2,3)
True
2**-3
pow(2,-3)
True
2**3**x5
pow(2,pow(3,x5))
True
2**-3**x6[-1]
pow(2,-pow(3,x6[((-1))]))
True
2**-3**x5+1
pow(2,-pow(3,x5))+1
True
(a+1)**2
pow((a+1),2)
True
((a+b)*c)**2
pow(((a+b)*c),2)
True
B**2
pow(B,2)
True
-B**2
(-pow(B,2))
True
(-B)**2
pow(((-B)),2)
True
B**-2
pow(B,-2)
True
B**(-2)
pow(B,((-2)))
True
B**2&&T**2*X
pow(B,2))&&(pow(T,2)*X
Traceback (most recent call last):
File "convert_to_pow.py", line 85, in <module>
print '**' not in xform and eval(xform) == eval(test)
File "<string>", line 1
pow(B,2))&&(pow(T,2)*X
^
SyntaxError: invalid syntax
如果您正在转换的代码中有更多极端情况,可能只需要对 operand
表达式进行更多调整,或添加更多运算符(如 &&
) infixNotation
表达式。
(请注意,您必须将 a**b**c
转换为 a**(b**c)
,因为链式求幂是从右到左计算的,而不是从左到右计算的。)
编辑:
引入 hack 以正确处理“-”和“**”之间的优先级。扩展测试以实际评估 before/after 个字符串。现在看起来更结实了。
我的要求是将**运算符改为幂函数
例如
1.Input -"B**2"
Output - power(B,2)
2."B**2&&T**2*X"
Output - power(B,2)
我写了下面的正则表达式来解决这个问题
rx=r"([a-zA-Z0-9]+)\*\*([a-zA-Z0-9()]+)"
result = regex.sub(rx, r"power(,)", expression, 0, regex.IGNORECASE | regex.MULTILINE)
但是上面的代码成功转换了类似于示例1和示例2的表达式,但是没有转换成(a+1)**2 or ((a+b)*c)**2
这样的表达式。我意识到正则表达式不是处理此类情况的最佳方式。而不是解析将是处理该问题的最佳方法。
我对 python 有点陌生。请指导我如何解决这个问题。
如果您需要处理一般的 Python 语法,请查看 ast
模块。您可能想要 ast.parse
the string into an abstract syntax tree and use an ast.NodeTransformer
来替换节点。但是,没有 ast.unparse
,因此您需要使用第三方依赖项或自己编写解析处理。
如果你只需要处理算术运算(特别是如果你想 拒绝 其他 Python 语法),你可能想用一些东西编写你自己的语法像 pyparsing
.
这听起来很熟悉,我想我在 pyparsing 邮件列表上处理过类似的问题,但我暂时找不到。但是尝试这样的事情:
from pyparsing import *
# define some basic operand expressions
number = Regex(r'\d+(\.\d*)?([Ee][+-]?\d+)?')
ident = Word(alphas+'_', alphanums+'_')
# forward declare our overall expression, since a slice could
# contain an arithmetic expression
expr = Forward()
slice_ref = '[' + expr + ']'
# define our arithmetic operand
operand = number | Combine(ident + Optional(slice_ref))
# parse actions to convert parsed items
def convert_to_pow(tokens):
tmp = tokens[0][:]
ret = tmp.pop(-1)
tmp.pop(-1)
while tmp:
base = tmp.pop(-1)
# hack to handle '**' precedence ahead of '-'
if base.startswith('-'):
ret = '-pow(%s,%s)' % (base[1:], ret)
else:
ret = 'pow(%s,%s)' % (base, ret)
if tmp:
tmp.pop(-1)
return ret
def unary_as_is(tokens):
return '(%s)' % ''.join(tokens[0])
def as_is(tokens):
return '%s' % ''.join(tokens[0])
# simplest infixNotation - may need to add a few more operators, but start with this for now
arith_expr = infixNotation( operand,
[
('-', 1, opAssoc.RIGHT, as_is),
('**', 2, opAssoc.LEFT, convert_to_pow),
('-', 1, opAssoc.RIGHT, unary_as_is),
(oneOf("* /"), 2, opAssoc.LEFT, as_is),
(oneOf("+ -"), 2, opAssoc.LEFT, as_is),
])
# now assign into forward-declared expr
expr <<= arith_expr.setParseAction(lambda t: '(%s)' % ''.join(t))
assert "2**3" == expr
assert "2**-3" == expr
# test it out
tests = [
"2**3",
"2**-3",
"2**3**x5",
"2**-3**x6[-1]",
"2**-3**x5+1",
"(a+1)**2",
"((a+b)*c)**2",
"B**2",
"-B**2",
"(-B)**2",
"B**-2",
"B**(-2)",
"B**2&&T**2*X",
]
x5 = 2
a,b,c = 1,2,3
B = 4
x6 = [3,2]
for test in tests:
print test
xform = expr.transformString(test)[1:-1]
print xform
print '**' not in xform and eval(xform) == eval(test)
print
打印:
2**3
pow(2,3)
True
2**-3
pow(2,-3)
True
2**3**x5
pow(2,pow(3,x5))
True
2**-3**x6[-1]
pow(2,-pow(3,x6[((-1))]))
True
2**-3**x5+1
pow(2,-pow(3,x5))+1
True
(a+1)**2
pow((a+1),2)
True
((a+b)*c)**2
pow(((a+b)*c),2)
True
B**2
pow(B,2)
True
-B**2
(-pow(B,2))
True
(-B)**2
pow(((-B)),2)
True
B**-2
pow(B,-2)
True
B**(-2)
pow(B,((-2)))
True
B**2&&T**2*X
pow(B,2))&&(pow(T,2)*X
Traceback (most recent call last):
File "convert_to_pow.py", line 85, in <module>
print '**' not in xform and eval(xform) == eval(test)
File "<string>", line 1
pow(B,2))&&(pow(T,2)*X
^
SyntaxError: invalid syntax
如果您正在转换的代码中有更多极端情况,可能只需要对 operand
表达式进行更多调整,或添加更多运算符(如 &&
) infixNotation
表达式。
(请注意,您必须将 a**b**c
转换为 a**(b**c)
,因为链式求幂是从右到左计算的,而不是从左到右计算的。)
编辑:
引入 hack 以正确处理“-”和“**”之间的优先级。扩展测试以实际评估 before/after 个字符串。现在看起来更结实了。