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

问题描述 投票:0回答:3

我的要求是把**操作符改为power函数

例如

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 or ((a+b)*c)**2
的表达式。我意识到正则表达式并不是处理这种情况的最佳方法。相反,解析将是处理该问题的最佳方法。 我对 python 有点陌生。请指导我如何解决这个问题。

python regex python-2.7 abstract-syntax-tree pyparsing
3个回答
2
投票

这听起来很熟悉,我想我在 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 来正确处理“-”和“**”之间的优先级。扩展测试以实际评估之前/之后的字符串。现在看起来更坚固了。


2
投票

如果您需要处理一般的 Python 语法,请查看

ast
模块。您可能希望将字符串
ast.parse
放入抽象语法树中,并使用
ast.NodeTransformer
来替换节点。不过,没有
ast.unparse
,因此您需要使用第三方依赖项或自己编写解析处理。

如果您只需要处理算术运算(特别是如果您想拒绝其他Python语法),您可能需要使用类似

pyparsing
之类的内容编写自己的语法。


0
投票

这个库应该可以解决您的问题 用于 PowerSyntax 转换的 JS 库

您可以在 Node js 中将程序作为与 Python 代码通信的模块运行(或者只是在 Py 中重写)。

© www.soinside.com 2019 - 2024. All rights reserved.