我有一个像这样的表达式/公式
std::string expr="((A>0) && (B>5 || C > 10))";
我做了一些研究,似乎如果A,B,C值已知,通过在C程序中嵌入Lua或Python,有 eval
函数可以替换A,B和C并返回 true
或 false
.
但是当我不知道所有的 Value 时会发生什么?让我们说A是已知的,它是-1 . 如果A为-1,则无论B或C的值如何,公式都将评估为“假” .
我可以在不事先知道所有变量的情况下评估公式吗?例如,如果A为10,则查找B的值并再次重新评估是有意义的 . 我们如何解决这些问题?想法?
12 回答
听起来你有两个挑战:
计算一些变量值很昂贵,因此您要避免计算评估表达式所不需要的值;和
您的表达式以字符串形式存在,在运行时组成,因此您可以't use C++'内置短路逻辑 .
这意味着您需要某种方式在运行时评估表达式,并且如果可能,您希望利用短路逻辑 . Python可能是一个很好的选择,如下面的例子所示 .
有一个简短的Python脚本(
evaluate.py
),它定义了一个evaluate()
函数,可以从C或C程序中调用它 .evaluate()
函数将尝试计算您给出的表达式(如果需要,将"&&"和"||"转换为"and"和"or") . 如果它需要一个尚未定义的变量,它将通过调用C / C程序中定义的get_var_value()
函数来检索该变量的值(然后缓存该值以供以后使用) .这种方法将使用正常的短路行为,因此它只会请求完成评估表达式所需的变量值 . 请注意,这不会重新排列表达式以选择评估它所需的最小变量集;它只是使用标准的短路行为 .
更新:我在最后添加了一个示例,它使用.cpp文件中的多行字符串文字定义Python脚本 . 如果您不想在可执行文件中安装单独的evaluate.py文件,这可能很有用 . 它还简化了Python初始化 .
以下脚本中的C / Python交互基于https://docs.python.org/2/extending/embedding.html和https://docs.python.org/2/c-api/arg.html中的代码 .
这是文件:
evaluate.py (Python脚本)
evaluate.c (您的主程序;也可以是evaluate.cpp,用g编译)
结果:
作为替代方案,您可以将所有这些代码组合到一个.cpp文件中,如下所示 . 这使用C 11中的多行字符串文字功能 .
自足 evaluate.cpp
我不知道有任何现有的库来处理这个问题 .
通常的方法是构建表达式树并评估可能的内容 - 类似于编译器中的常量折叠:https://en.wikipedia.org/wiki/Constant_folding
其中一个重要方面是知道变量的允许值,从而知道允许的部分评估,例如,
x*0
(和0*x
)是0
如果x
是整数或有限浮点数,但如果x
是IEEE浮点数(因为它可能是Nan或无穷大),或者如果x
可能是矩阵,则无法求值,因为[1,1]*0
是[0,0]
不是标量0
.一种方法是将表达式解析为树并评估树 . 将全面评估已知所有变量的Subexpressions . 效果将是简化树 .
在您的示例中,树的顶部有
&&
,有两个子树,左侧是A>0
的树 . 为了评估树,我们评估左子树,它返回-1,因此我们不需要评估正确的子树,因为运算符是&&
. 整棵树评估为假 .我不明白你想要做什么或理解什么但我对ivan_pozdeev关于 short-circuit evaluation 和 lazy evaluation 没问题 .
布尔表达式为 from the left to the right ,当结果已知时,评估停止并忽略右侧的内容 .
使用Python:
给
但
给出错误“名称'B'未定义” .
因此,根据我对你的问题的理解,你需要类似的东西
即你的表达式必须拆分,以便您只使用已知值 .
你可以这样做:
稍后详细介绍......
在我看来答案是肯定的,是的,你可以尝试用缺少的信息来评估表达式 . 您需要定义符号查找失败时会发生什么 .
在这种情况下,您将需要一个布尔表达式求值程序和一个符号表,以便求值程序可以查找符号来执行表达式 .
如果您成功查找所有符号,结果将为true或false . 如果您未能查找符号,则处理该情况,可能返回None,nullptr或引发/抛出异常 .
我相信你可以在你的c程序中嵌入python解释器并调用一个函数来评估表达式,更重要的是,你可以给它用作符号表的dict . 如果调用返回结果,则能够找到足够的符号或结果的快捷方式,否则会引发c代码可以检测到的异常 .
您可以在python中对函数进行原型设计,如果方法按照您想要的方式工作,则进行评估,然后嵌入 .
或者您可以使用语法,词法分析器,解析器和电梯在c中完成所有操作 .
虽然这是您的解决方案的一个非常粗略的实现,但它完全适合您的情况,虽然使用了大量的
if else
和异常处理 .现在解释我的代码,
main_func
返回另一个函数,用于计算字符串中给定的表达式 . 虽然此处字符串已经过硬编码,但您始终可以将其作为参数传递给函数,并将eval
中的字符串替换为参数 .在
doer
中,调用main_func
返回的函数,如果抛出NameError
,则在先前条件为假并且要计算新值的情况下发生,则返回需要计算的特定变量 . 所有这些都在actual_evaluator
中检查,其中变量的值是通过getter
中可以定义的函数get_variable_name
获取的 . 在我的代码中,我必须通过其他方式评估各种变量,以便您可以调用相应的函数 .由于短路行为,即使没有定义所有包含的值,Python也可以评估表达式(如果可能的话) . 如果没有,它会引发异常:
但表达式从左到右评估:
我过去曾为此做过“自己动手”的方法 . 简单的事情并不困难;您只需创建自己的对象,实现魔术数学方法并跟踪其他对象 .
如果您需要更全功能的东西,sympy项目旨在进行符号数学...
我来看看sympy或其他计算机代数系统 . 我相信,对于pxeression和短路评估的代数简化将允许您评估可能获得结果的所有情况 . 在某些情况下,您需要知道某个变量的值 . 例如,如果你有一个像== b这样的简单表达式,那么在不知道a和b的值的情况下你将无法取得进展 . 然而,如(a> = 0)||(a <= 0),代数简化将导致为假,即a不是NAN或其他不等于自身的值 .
我从这个问题中假设
你有一个逻辑表达式,取决于各种函数的结果;
您多次使用其中某些函数的值(可能在评估此表达式之前,或者可能在此表达式中),因此您希望存储其结果以避免两次调用它们;和
您想要评估逻辑表达式,以及您希望检索和存储以前未运行过的函数的值,但只有足够的值来评估表达式(使用正常的短路行为) .
我在一个不同的答案中提到,你可能最好只使用C中的内置短路行为 . 要做到这一点并实现目标2,您需要在逻辑表达式中使用函数而不是变量 . 这样,当表达式需要时,您可以触发计算缺失值 .
以下是两种方法 . 第一个使用通用缓存包装器包装慢速函数 . 第二个定义了一个自定义缓存你的每个慢功能的助手 . 编译完成后,应使用A,B和C值调用其中任何一个进行测试,例如:
evaluate_cached 10 9 -1
. 他们都会按照你想要的方式行事 .evaluate_cached.cpp
evaluate_helpers.c