首页 > 解决方案 > 从字符串中读取方程并解析它

问题描述

我有像这样以字符串形式编写的布尔数学表达式

"A and B or C and not D"
"A and (B or C) and not D"

我想用python解决它,“and”、“or”和“and not”和“or not”运算符将调用不同的函数。我想不出任何可以解决这个问题的想法。

我可以用 split 打破等式,但下一步是什么?

更新: A 和 B 和 other 可以包含一些单词,这些单词将返回一个列表,并在该列表上执行“and”、“or”和其他操作。

我已经创建了该列表,我可以解决 2 变量问题,但是当它有多个组合时如何解决它。

标签: pythonmathboolean

解决方案


符号包Sympy似乎是一个不错的选择。

在其基本形式中,sympy 需要,andor,和,可以在调用解析器之前被替换。请注意,如果“Brandon”之类的名称包含“and”,则简单的字符串替换将无法正常工作。not&|~

之后,您可以使用 sympy 进行任何类型的简化、操作和替换,就像任何其他数学表达式一样。

一个例子:

from sympy.parsing.sympy_parser import parse_expr

str = "Alice and (Bob or Carla) and not David"
str = str.replace('and', '&').replace('or', '|').replace('not', '~')
expr = parse_expr(str)

print(expr)
print(expr.subs({'Alice': True, 'Bob': False}))

输出:

Alice & ~David & (Bob | Carla)
Carla & ~David

在评论中,您要求对集合而不是布尔值进行操作。小数集在 Sympy 中表示为FiniteSet. 通常,它们需要使用函数符号来编写:Union(arg1, arg2, arg3, ...). 但是我们可以使用布尔运算符AndOrNot确实允许中缀表示法构造一个表达式,然后像设置操作一样进行评估。

from sympy.parsing.sympy_parser import parse_expr
from sympy import Atom, FiniteSet, And, Or, Not, Union, Intersection, Complement


def evaluate_expr(expr, replacements, complete_set):
    if isinstance(expr, Atom):
        return expr.subs(replacements)
    elif expr.func == Or:
        return Union(*[evaluate_expr(a, replacements, complete_set) for a in expr.args])
    elif expr.func == And:
        return Intersection(*[evaluate_expr(a, replacements, complete_set) for a in expr.args])
    elif expr.func == Not:
        return Complement(complete_set, evaluate_expr(expr.args[0], replacements, complete_set))

replacements = {'Alice': {1, 2}, 'Bob': {1, 2, 3}, 'Carla': {5}, 'David': {1, 4, 6}}
for r in replacements:
    # convert the replacements to their Sympy representation
    replacements[r] = FiniteSet(*replacements[r])
print("Replacements:", replacements)
complete_set = Union(*replacements.values()) # the set of all possible values is used to implement "not"

tests = ["Alice", "Alice or Bob", "Alice and Bob and Carla", "Alice and (Bob or Carla) and not David"]
for test in tests:
    expr = parse_expr(test.replace('and', '&').replace('or', '|').replace('not', '~'))
    print(expr)
    print(" --->", evaluate_expr(expr, replacements, complete_set))

输出:

Replacements: {'Alice': FiniteSet(1, 2), 'Bob': FiniteSet(1, 2, 3), 'Carla': FiniteSet(5), 'David': FiniteSet(1, 4, 6)}
Alice
 ---> FiniteSet(1, 2)
Alice | Bob
 ---> FiniteSet(1, 2, 3)
Alice & Bob & Carla
 ---> EmptySet
Alice & ~David & (Bob | Carla)
 ---> FiniteSet(2)

推荐阅读