Python 使用解析将**运算符更改为幂函数?

Python 使用解析将**运算符更改为幂函数?,python,regex,python-2.7,abstract-syntax-tree,pyparsing,Python,Regex,Python 2.7,Abstract Syntax Tree,Pyparsing,我的要求是将**运算符更改为幂函数 比如说 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(\1,\2)", expression, 0, regex.IGNORECASE |

我的要求是将**运算符更改为幂函数

比如说

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(\1,\2)", expression, 0, regex.IGNORECASE | regex.MULTILINE)
但是上面的代码成功地转换了类似于示例1和示例2的表达式,但未能转换类似于
(a+1)**2或((a+b)**c)**2的表达式。我意识到正则表达式不是处理这种情况的最佳方法。相反,解析将是处理该问题的最佳方法。

我对python有点陌生。请指导我如何解决这个问题。

如果需要处理一般python语法,请查看
ast
模块。您可能希望将字符串转换为抽象语法树,并使用替换节点。但是没有
ast.unparse
,因此您需要使用第三方依赖项或自己编写解析处理


如果您只需要处理算术运算(特别是如果您想拒绝其他Python语法),您可能需要使用类似的东西编写自己的语法。

这听起来很熟悉,我想我在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
从pyparsing导入*
#定义一些基本操作数表达式
number=Regex(r'\d+(\.\d*)?([Ee][+-]?\d+))
ident=单词(字母+字母,字母+字母)
#向前声明我们的整体表达式,因为切片可以
#包含算术表达式
expr=Forward()
切片_ref='['+expr+']
#定义算术操作数
操作数=数字|合并(标识+可选(切片_参考))
#解析操作以转换已解析的项
def将_转换为_pow(令牌):
tmp=令牌[0][:]
ret=tmp.pop(-1)
tmp.pop(-1)
而tmp:
基本=tmp.pop(-1)
#hack在“-”之前处理“**”优先级
如果base.startswith('-'):
ret='-pow(%s,%s)'(基[1:],ret)
其他:
ret='功率(%s,%s)'(基准,ret)
如果tmp:
tmp.pop(-1)
回程网
按原样定义一元(代币):
返回“(%s)”%''。加入(令牌[0])
def原样(代币):
返回“%s”%”。加入(令牌[0])
#最简单的infixNotation-可能需要添加几个运算符,但现在就从这个开始
arith_expr=infix表示法(操作数,
[
(“-”,1,opAssoc.RIGHT,as_is),
(“**”,2,opAssoc.LEFT,将_转换为_pow),
(“-”,1,opAssoc.RIGHT,一元原样),
(其中一个(“*/”,2,opAssoc.LEFT,如图所示),
(其中一个(“+-”,2,opAssoc.LEFT,如图所示),
])
#现在分配到正向声明的表达式中

expr好的,如果应用于
**
的表达式非常简单(仅限数字/变量),这很容易:逐字符读取和解析操作数,然后在遇到单个星号时,检查它是乘法还是幂函数。否则,您将不得不为此编写一个功能齐全的表达式解析器。谢谢您的评论。正则表达式程序成功地处理了这种类型的简单表达式这应该处理什么类型的输入?在第二个示例中有一个
&&
,因此这不是Python语法或基本算法。(那么它的输出真的应该是
功率(B,2)
?)它(不幸的是)看起来OP需要的不仅仅是基本的python表达式,因为第二个输入有一个
&&
操作符。是的,它需要的不仅仅是基本的python表达式使用时要小心-看起来这不能正确处理
2**-3
这样的情况…现在看起来好多了-我记得必须做一个hack来处理“-”和“**”操作符的优先级。Python解释器做了类似的事情,上次我看了。非常感谢。它真的很有帮助。它正在处理大部分条件。但是测试用例X(n1)*2**f(n1)失败。是的,您需要扩展操作数的定义,以包括函数调用的格式。比如说
fn_call=ident+'('+可选(分隔的_列表(expr))+')”
。添加解析操作以连接调用的各个部分:
fn\u call.addParseAction(''.join())
。然后
operand=number | fn|u call | Combine(ident+可选(slice_ref))
。非常感谢您的理解
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