评估 python 中没有“*”符号的(几乎是代数的)表达式
Evaluate an (almost algebraic) expression without the '*' symbol in python
我在value.txt
中有以下内容:
2A-25X-8A+34X-5B+11B
如果我通过终端 bash 使用 MetaFont 如下:
#mf
This is METAFONT, Version 2.7182818 (TeX Live 2019/Arch Linux) (preloaded base=mf)
**expr
(/usr/share/texmf-dist/fonts/source/public/knuth-lib/expr.mf
gimme an expr: 2A-25X-8A+34X-5B+11B
>> 6B+9X-6A
gimme an expr:
我可以计算字母和数字之间没有“*”符号的表达式。
我想要的是尽可能干净和经济地使用 Python 来做到这一点,但仍然不使用“*”。
我还没有找到任何关于它的信息。
我也希望它是一种可以用 with open
、print =
和 r
.
实现的语法
编辑
一个可能的想法是这样的:
with open ("value.txt", "r") as value:
data = value.read()
#some python method for evaluate value.txt expression and save in variable value2
print = (value2)
总是对有关解析算术的问题感兴趣。这是一个基于 pyparsing 的解决方案(尽管比您希望的要长一些,而且使用的不仅仅是 with、open 等)。
前 30 行定义了一个 class 用于计算变量,支持加、减和乘以整数。 (整数被建模为带有变量“”的 Tally。)
接下来的 30 行定义了实际的解析器,以及将解析的标记转换为累积 Tally 对象的解析时操作。
最后 25 行是测试,包括您的示例表达式。
解析器真正的"smarts"在infixNotation
方法中,实现了对各种运算符的解析,包括运算符优先级的处理和分组
与 () 的。使用“3A”表示“3 乘以 A”是通过传递 None
作为乘法运算符来完成的。这也支持像“2(A+2B)”这样的结构来给出“2A+4B”。
import pyparsing as pp
# special form of dict to support addition, subtraction, and multiplication, plus a nice repr
class Tally(dict):
def __add__(self, other):
ret = Tally(**self)
for k, v in other.items():
ret[k] = ret.get(k, 0) + v
if k and ret[k] == 0:
ret.pop(k)
return ret
def __mul__(self, other):
if self[''] == 0:
return Tally()
ret = Tally(**other)
for k in ret:
ret[k] *= self['']
return ret
def __sub__(self, other):
return self + MINUS_1 * other
def __repr__(self):
ret = ''.join("{}{}{}".format("+" if coeff > 0 else "-", str(abs(coeff)) if abs(coeff) != 1 else "", var)
for var, coeff in sorted(self.items()) if coeff)
# leading '+' signs are unnecessary
ret = ret.lstrip("+")
return ret
MINUS_1 = Tally(**{'': -1})
var = pp.oneOf(list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
# convert var to a Tally of 1
var.addParseAction(lambda t: Tally(**{t[0]: 1}))
integer = pp.pyparsing_common.integer().addParseAction(lambda tokens: Tally(**{'': tokens[0]}))
def add_terms(tokens):
parsed = tokens[0]
ret = parsed[0]
for op, term in zip(parsed[1::2], parsed[2::2]):
if op == '-':
ret -= term
else:
ret += term
return ret
def mult_terms(tokens):
coeff, var = tokens[0]
return coeff * var
# only the leading minus needs to be handled this way, all others are handled
# as binary subtraction operators
def leading_minus(tokens):
parsed = tokens[0]
return MINUS_1 * parsed[1]
leading_minus_sign = pp.StringStart() + "-"
operand = var | integer
expr = pp.infixNotation(operand,
[
(leading_minus_sign, 1, pp.opAssoc.RIGHT, leading_minus),
(None, 2, pp.opAssoc.LEFT, mult_terms),
(pp.oneOf("+ -"), 2, pp.opAssoc.LEFT, add_terms),
])
expr.runTests("""\
B
B+C
B+C+3B
2A
-2A
-3Z+42B
2A+4A-6A
2A-25X-8A+34X-5B+11B
3(2A+B)
-(2A+B)
-3(2A+B)
2A+12
12
-12
2A-12
(5-3)(A+B)
(3-3)(A+B)
""")
给出输出(runTests
回显每个测试行,然后是解析结果):
B
[B]
B+C
[B+C]
B+C+3B
[4B+C]
2A
[2A]
-2A
[-2A]
-3Z+42B
[42B-3Z]
2A+4A-6A
[]
2A-25X-8A+34X-5B+11B
[-6A+6B+9X]
3(2A+B)
[6A+3B]
-(2A+B)
[-2A-B]
-3(2A+B)
[-6A-3B]
2A+12
[12+2A]
12
[12]
-12
[-12]
2A-12
[-12+2A]
(5-3)(A+B)
[2A+2B]
(3-3)(A+B)
[]
要展示如何使用 expr 解析表达式字符串,请参阅此代码:
result = expr.parseString("2A-25X-8A+34X-5B+11B")
print(result)
print(result[0])
print(type(result[0]))
# convert back to dict
print({**result[0]})
打印:
[-6A+6B+9X]
-6A+6B+9X
<class '__main__.Tally'>
{'B': 6, 'A': -6, 'X': 9}
我在value.txt
中有以下内容:
2A-25X-8A+34X-5B+11B
如果我通过终端 bash 使用 MetaFont 如下:
#mf
This is METAFONT, Version 2.7182818 (TeX Live 2019/Arch Linux) (preloaded base=mf)
**expr
(/usr/share/texmf-dist/fonts/source/public/knuth-lib/expr.mf
gimme an expr: 2A-25X-8A+34X-5B+11B
>> 6B+9X-6A
gimme an expr:
我可以计算字母和数字之间没有“*”符号的表达式。
我想要的是尽可能干净和经济地使用 Python 来做到这一点,但仍然不使用“*”。
我还没有找到任何关于它的信息。
我也希望它是一种可以用 with open
、print =
和 r
.
编辑
一个可能的想法是这样的:
with open ("value.txt", "r") as value:
data = value.read()
#some python method for evaluate value.txt expression and save in variable value2
print = (value2)
总是对有关解析算术的问题感兴趣。这是一个基于 pyparsing 的解决方案(尽管比您希望的要长一些,而且使用的不仅仅是 with、open 等)。
前 30 行定义了一个 class 用于计算变量,支持加、减和乘以整数。 (整数被建模为带有变量“”的 Tally。)
接下来的 30 行定义了实际的解析器,以及将解析的标记转换为累积 Tally 对象的解析时操作。
最后 25 行是测试,包括您的示例表达式。
解析器真正的"smarts"在infixNotation
方法中,实现了对各种运算符的解析,包括运算符优先级的处理和分组
与 () 的。使用“3A”表示“3 乘以 A”是通过传递 None
作为乘法运算符来完成的。这也支持像“2(A+2B)”这样的结构来给出“2A+4B”。
import pyparsing as pp
# special form of dict to support addition, subtraction, and multiplication, plus a nice repr
class Tally(dict):
def __add__(self, other):
ret = Tally(**self)
for k, v in other.items():
ret[k] = ret.get(k, 0) + v
if k and ret[k] == 0:
ret.pop(k)
return ret
def __mul__(self, other):
if self[''] == 0:
return Tally()
ret = Tally(**other)
for k in ret:
ret[k] *= self['']
return ret
def __sub__(self, other):
return self + MINUS_1 * other
def __repr__(self):
ret = ''.join("{}{}{}".format("+" if coeff > 0 else "-", str(abs(coeff)) if abs(coeff) != 1 else "", var)
for var, coeff in sorted(self.items()) if coeff)
# leading '+' signs are unnecessary
ret = ret.lstrip("+")
return ret
MINUS_1 = Tally(**{'': -1})
var = pp.oneOf(list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
# convert var to a Tally of 1
var.addParseAction(lambda t: Tally(**{t[0]: 1}))
integer = pp.pyparsing_common.integer().addParseAction(lambda tokens: Tally(**{'': tokens[0]}))
def add_terms(tokens):
parsed = tokens[0]
ret = parsed[0]
for op, term in zip(parsed[1::2], parsed[2::2]):
if op == '-':
ret -= term
else:
ret += term
return ret
def mult_terms(tokens):
coeff, var = tokens[0]
return coeff * var
# only the leading minus needs to be handled this way, all others are handled
# as binary subtraction operators
def leading_minus(tokens):
parsed = tokens[0]
return MINUS_1 * parsed[1]
leading_minus_sign = pp.StringStart() + "-"
operand = var | integer
expr = pp.infixNotation(operand,
[
(leading_minus_sign, 1, pp.opAssoc.RIGHT, leading_minus),
(None, 2, pp.opAssoc.LEFT, mult_terms),
(pp.oneOf("+ -"), 2, pp.opAssoc.LEFT, add_terms),
])
expr.runTests("""\
B
B+C
B+C+3B
2A
-2A
-3Z+42B
2A+4A-6A
2A-25X-8A+34X-5B+11B
3(2A+B)
-(2A+B)
-3(2A+B)
2A+12
12
-12
2A-12
(5-3)(A+B)
(3-3)(A+B)
""")
给出输出(runTests
回显每个测试行,然后是解析结果):
B
[B]
B+C
[B+C]
B+C+3B
[4B+C]
2A
[2A]
-2A
[-2A]
-3Z+42B
[42B-3Z]
2A+4A-6A
[]
2A-25X-8A+34X-5B+11B
[-6A+6B+9X]
3(2A+B)
[6A+3B]
-(2A+B)
[-2A-B]
-3(2A+B)
[-6A-3B]
2A+12
[12+2A]
12
[12]
-12
[-12]
2A-12
[-12+2A]
(5-3)(A+B)
[2A+2B]
(3-3)(A+B)
[]
要展示如何使用 expr 解析表达式字符串,请参阅此代码:
result = expr.parseString("2A-25X-8A+34X-5B+11B")
print(result)
print(result[0])
print(type(result[0]))
# convert back to dict
print({**result[0]})
打印:
[-6A+6B+9X]
-6A+6B+9X
<class '__main__.Tally'>
{'B': 6, 'A': -6, 'X': 9}