从C ++调用Python或Lua来评估表达式,仅在需要时计算未知变量

我有一个像这样的表达式/公式

 std::string expr="((A>0) && (B>5 || C>10))";

我已经做了一些研究,似乎如果嵌入Lua或Python在C ++程序中,有eval函数可以替换A、B和C并返回truefalse

但是当我不知道所有的值时会发生什么?假设A已知且为-1。如果A为-1,则无论B或C的值如何,公式都将计算为“false”。

我可以在不事先知道所有变量的情况下评估公式吗?例如,如果A为10,则寻找B的值并重新评估再次是有意义的。我们如何解决这些问题?思路?

点赞
用户6387170
用户6387170

所以我理解你的问题是想要类似于以下代码:

if (A>0) {
  B = getB();
  C = getC();
  if (B>23 || C==11)
    explode();
}

也就是你的表达式必须被拆分成只处理已知的值。

2017-04-20 10:25:57
用户107090
用户107090

一种方法是将表达式解析成一棵树并计算树。对于所有变量都已知的子表达式将被完全计算。效果将是简化树。

在您的示例中,树的顶部为 &&,有两个子树,左边的子树是 A>0 的树。为了计算树,我们计算左子树,返回-1,因此我们不需要计算右子树,因为运算符是&&。整个树计算结果为_false_。

2017-04-20 10:50:20
用户550094
用户550094

你可以这样做:

class LazyValues():

    def __init__(self):
        self._known_values = {}

    def __getitem__(self, var):
        try:
            return self._known_values[var]
        except KeyError:
            print("Evaluating %s..." % var)
            return self._known_values.setdefault(var, eval(var))

def lazy_eval(expr, lazy_vars):
    for var in lazy_vars:
        expr  = expr.replace(var, "lazy_values['%s']" % var)
        #看起来是 ((lazy_value['A']>0) && (lazy_value['B']>5 || lazy_value['C'] > 10))

    lazy_values = LazyValues()
    return eval(expr)

lazy_eval("((A>0) and (B>5 or C > 10))", lazy_vars=['A', 'B', 'C'])

# Evaluating A...
# ....
# NameError: name 'A' is not defined

A = -1
lazy_eval("((A>0) and (B>5 or C > 10))", lazy_vars=['A', 'B', 'C'])

# Evaluating A...
# False

A = 5
B = 6
lazy_eval("((A>0) and (B>5 or C > 10))", lazy_vars=['A', 'B', 'C'])

# Evaluating A...
# Evaluating B...
# True

更多细节稍后...

2017-04-20 11:33:42
用户5609977
用户5609977

虽然这是一个非常粗糙的解决方案实现,但它完全适合您的情况,尽管使用了很多 if else 和异常处理。

def main_func():
    def checker(a, b=None, c=None):
        if a is None:
            del a
        if b is None:
            del b
        if c is None:
            del c
        d = eval('a>0 and (b>5 or c>10)')
        return d
    return checker

def doer(a=None, b=None, c=None):
    try:
        return main_func()(a,b,c)
    except NameError as e:
        if "'a' is not" in str(e):
            return 'a'
        elif "'b' is not" in str(e):
            return 'b'
        elif "'c' is not" in str(e):
            return 'c'

def check_ret(ret):
    return type(ret) == bool

def actual_evaluator():
    getter = {
        "a": get_a,
        "b": get_b,
        "c": get_c
    }
    args = []
    while True:
        ret = doer(*tuple(args))
        if not check_ret(ret):
            val = getter[ret]()
            args.append(val)
        else:
            return ret

if __name__ == '__main__':
        print actual_evaluator()

现在解释我的代码,main_func 返回另一个函数,该函数用于评估给定的字符串表达式。在这里,字符串已硬编码,但您可以始终将其作为参数传递给函数,并用 eval 内部的参数替换字符串。

doer 中,调用 main_func 返回的函数,并且如果抛出 NameError,这在以前的条件为假且需要计算新值时会发生,则返回需要计算的特定变量。所有这些都在 actual_evaluator 中进行检查,其中通过一些函数 get_variable_name 获取变量的值 ,可以在 getter 字典中定义。在我的代码中,我使用随机数字来检查有效性,但是像您所说,您必须通过其他方式评估各个变量,因此您可以调用相应的函数。

2017-04-26 20:50:07
用户562106
用户562106

我认为答案是肯定的,你可以尝试评估带有缺失信息的表达式。然而,当一个符号查找失败时,你需要定义它所发生的情况。

在你的情况下,你需要一个布尔表达式评估器和一个符号表,以便评估器可以查找符号并执行表达式。

如果你成功查找了所有符号,结果将为true或false。如果你未能查找到一个符号,那么处理这种情况,可能返回None、nullptr,或引发/抛出异常等。

我认为你可以在你的c++程序中嵌入python解释器,并调用一个函数来评估表达式。更重要的是,你可以给它一个用作符号表的字典。如果调用返回结果,则它可以找到足够的符号或简化到一个结果,否则它将引发一个异常,你的c++代码可以检测到。

你可以在python中原型函数来评估你想要的方式,然后进行嵌入。

或者你可以在c++中通过语法、词法分析器、解析器和升级机来实现。

2017-04-26 21:00:39
用户648265
用户648265

由于短路行为,如果可能的话,即使没有定义所有包含的值,Python也可以评估一个表达式。如果不可能,则会引发异常:

In [1]: a= False

In [3]: a and b
Out[3]: False

In [4]: a or b
NameError: name 'b' is not defined

但是表达式是从左到右评估的:

In [5]: b and a
NameError: name 'b' is not defined
2017-04-26 21:37:26
用户5603247
用户5603247

我不知道有任何现有的可用库可以处理这种情况。

通常的方法是建立一个表达式树,然后评估可行的部分,类似于编译器中的常量折叠: https://en.wikipedia.org/wiki/Constant_folding

其中一个重要的方面是要知道变量的允许值,以及允许的部分评估,例如,如果x是整数或有限的浮点数,则x*0(和0*x)为0,但如果x是IEEE浮点数(因为它可能是NaN或无穷大),或者如果x可以是矩阵,则无法评估为标量0,因为[1,1]*0[0,0]而不是标量0

2017-04-28 07:45:44
用户3577601
用户3577601

我过去曾自己处理这个问题。对于简单事情来说并不难;你只需创建自己的对象,实现魔术数学方法并跟踪其他对象就可以了。

如果您需要更全面的功能,那么 sympy 项目专门设计用于执行符号数学计算...

2017-04-30 00:33:04
用户7908091
用户7908091

我并不确定你想做什么或者是否理解,但我同意 ivan_pozdeev 关于 短路评估(short-circuit evaluation)懒惰评估(lazy evaluation) 的观点。

一个布尔表达式从左往右依次评估,当结果已知时,评估停止并忽略右边的值。

在 Python 中:

E = "(A > 0) and (B > 5 or C > 10)"
A = -1
print(eval(E))

会返回

False

但是

E = "(A > 0) and (B > 5 or C > 10)"
A = 1
print(eval(E))

会报错 " name 'B' is not defined".

2017-05-01 08:30:12
用户2594812
用户2594812

我建议你看看SymPy或其他计算机代数系统。我相信代数简化和短路求值将允许你在所有可能获得结果的情况下进行评估。有些情况下,你需要知道某个变量的值。例如,如果你有一个简单的表达式像a == b,如果不知道a和b的值,你将无法进行任何进展。然而,如果你有一个表达式像(a >= 0) ||(a <= 0),代数简化将得出true,假设a不是NAN或某个不等于它本身的其他值。

2017-05-01 19:10:13