从C ++调用Python或Lua来评估表达式,仅在需要时计算未知变量
我有一个像这样的表达式/公式
std::string expr="((A>0) && (B>5 || C>10))";
我已经做了一些研究,似乎如果嵌入Lua或Python在C ++程序中,有eval函数可以替换A、B和C并返回true或false。
但是当我不知道所有的值时会发生什么?假设A已知且为-1。如果A为-1,则无论B或C的值如何,公式都将计算为“false”。
我可以在不事先知道所有变量的情况下评估公式吗?例如,如果A为10,则寻找B的值并重新评估再次是有意义的。我们如何解决这些问题?思路?
一种方法是将表达式解析成一棵树并计算树。对于所有变量都已知的子表达式将被完全计算。效果将是简化树。
在您的示例中,树的顶部为 &&,有两个子树,左边的子树是 A>0 的树。为了计算树,我们计算左子树,返回-1,因此我们不需要计算右子树,因为运算符是&&。整个树计算结果为_false_。
你可以这样做:
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
更多细节稍后...
虽然这是一个非常粗糙的解决方案实现,但它完全适合您的情况,尽管使用了很多 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 字典中定义。在我的代码中,我使用随机数字来检查有效性,但是像您所说,您必须通过其他方式评估各个变量,因此您可以调用相应的函数。
我认为答案是肯定的,你可以尝试评估带有缺失信息的表达式。然而,当一个符号查找失败时,你需要定义它所发生的情况。
在你的情况下,你需要一个布尔表达式评估器和一个符号表,以便评估器可以查找符号并执行表达式。
如果你成功查找了所有符号,结果将为true或false。如果你未能查找到一个符号,那么处理这种情况,可能返回None、nullptr,或引发/抛出异常等。
我认为你可以在你的c++程序中嵌入python解释器,并调用一个函数来评估表达式。更重要的是,你可以给它一个用作符号表的字典。如果调用返回结果,则它可以找到足够的符号或简化到一个结果,否则它将引发一个异常,你的c++代码可以检测到。
你可以在python中原型函数来评估你想要的方式,然后进行嵌入。
或者你可以在c++中通过语法、词法分析器、解析器和升级机来实现。
由于短路行为,如果可能的话,即使没有定义所有包含的值,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
我不知道有任何现有的可用库可以处理这种情况。
通常的方法是建立一个表达式树,然后评估可行的部分,类似于编译器中的常量折叠: https://en.wikipedia.org/wiki/Constant_folding
其中一个重要的方面是要知道变量的允许值,以及允许的部分评估,例如,如果x是整数或有限的浮点数,则x*0(和0*x)为0,但如果x是IEEE浮点数(因为它可能是NaN或无穷大),或者如果x可以是矩阵,则无法评估为标量0,因为[1,1]*0是[0,0]而不是标量0。
我过去曾自己处理这个问题。对于简单事情来说并不难;你只需创建自己的对象,实现魔术数学方法并跟踪其他对象就可以了。
如果您需要更全面的功能,那么 sympy 项目专门设计用于执行符号数学计算...
我并不确定你想做什么或者是否理解,但我同意 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".
我建议你看看SymPy或其他计算机代数系统。我相信代数简化和短路求值将允许你在所有可能获得结果的情况下进行评估。有些情况下,你需要知道某个变量的值。例如,如果你有一个简单的表达式像a == b,如果不知道a和b的值,你将无法进行任何进展。然而,如果你有一个表达式像(a >= 0) ||(a <= 0),代数简化将得出true,假设a不是NAN或某个不等于它本身的其他值。
- Lua 虚拟机加密load(string.dump(function)) 后执行失败问题如何解决
- 我想创建一个 Nginx 规则,禁止访问
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?

所以我理解你的问题是想要类似于以下代码:
if (A>0) { B = getB(); C = getC(); if (B>23 || C==11) explode(); }也就是你的表达式必须被拆分成只处理已知的值。