查找可以简化的过于复杂的布尔表达式的工具

Tools looking for overly complex boolean expression that can be simplified?

本文关键字:复杂 工具 布尔表达式 查找 于复杂      更新时间:2023-10-16

是否有任何c++代码解析器寻找可以使用布尔代数简化的布尔表达式?

我知道编译器已经这样做了,但如果有一个工具提供这样的东西,这样就可以实际提高代码的可读性,那就太好了。

人类。

你想提高可读性,因为可读性主要是人的事情,所以应该由人来教。

请更有经验的开发人员检查你的表达式并给出提示。

例如,查看我在这里的回答:测试值是否落在阈值内的最佳方法(性能方面)是什么?

虽然它不能直接在C/c++布尔表达式上工作,但我发现有一个工具对于简化复杂的布尔逻辑非常有用,那就是logic Friday。不幸的是它只支持windows,但幸运的是它是免费的。

您可以通过减少需要检查的"if"的数量来使代码更高效。但更简化和更好的可读性不可能自动实现。

http://www.freewarepalm.com/educational/booleanfunctionsimplificationtool.shtml

可能值得一试。

然而,通常最好自己做——可读性和可理解性更重要——让编译器来简化。

这真是个坏主意!程序员编写的代码反映了他们的思维过程。因此,人类编写的布尔表达式已经自动优化为人类理解。任何试图在程序上改进这一点的尝试都注定要失败。它可能有意义的唯一上下文中是工具生成的源代码的后处理。

您需要的是一种工具,它可以解析c++,确定其符号的含义,挑选布尔方程,并对它们应用不违反语义的布尔简化规则。

一个可以做到这一点的工具是我们的DMS软件再造工具包及其c++前端。DMS被设计用来对代码进行程序分析和源到源的转换。使用c++前端,它可以将c++解析为ast,构建符号表,推断表达式的类型,并应用重写规则。

可以这样重写规则:

domain Cpp.  -- tell DMS to use the C++ front end
rule factor_common_and_term(e1: condition, e2:condition, e3: condition):
        disjunctive_expression -> disjunctive_expression =
 " e1 && e2 ||  e1 && e3 " ->  " e1 && ( e2 || e3 ) "
 if no_side_effects(e1) / no_side_effects(e2);

提出一个共同的条件。该规则的名称为"factor_common_and_term",以将其与可能编写的数百条其他规则(例如"distribute_term"等)区分开来。e1、e2、e3是表示任意子表达式(根据语法规则属于"condition"类型)的元变量。重写只操作disjunction_expressions;你可以让它成为"单纯的表达式"但这样你就不会得到嵌套在其他条件表达式中的析取。重写有一个模式(左)和一个替换(右),它们都用元引号"包装起来,以区分模式中的c++代码和它周围的规则语言语法。e1是c++语法的转义,指示元变量可以匹配的位置。元变量匹配相应类别的任何语法,因此看到e1的地方可以是一个任意复杂的"条件"。e1在模式中被提及两次的事实迫使它们的出现是相同的。

一个人可以写一组重写规则来编码关于简化任意复杂布尔方程的知识;几十条规则就够了。我们已经将这些应用于具有数十万项的非c++布尔方程系统,以及C和c++预预处理条件。

对于c++,您需要检查重写是否安全,如果e1有副作用,或者e2有副作用,则不安全。这个检查是通过一个辅助函数调用来完成的,这个函数调用必须以保守的方式确定这个答案。对于c++来说,确定没有副作用实际上是相当复杂的:您必须知道表达式的所有元素是什么,并且它们都没有副作用。

可以使用DMS的属性语法(有组织的树爬行)来执行此检查,该语法检查所有表达式元素。简单的常量和变量(为此需要一个符号表)不需要。函数调用(包括构造函数等)可以;必须找到它们的定义(同样需要一个符号表),并进行类似的处理。表达式元素可能调用单独编译的函数;在这种情况下,保守的回答是"不知道",因此"假设有副作用"。DMS实际上可以同时读取多个编译单元,因此可以找到一个单独编译的函数,解析/符号解析,并爬行,如果您想要那么做的话。

所以布尔重写部分很简单;副作用分析没有。

我们已经使用DMS对c++代码进行了大量的修改;我们经常通过对像这样的复杂分析做出假设来作弊。通常我们感到惊讶的方式和程序员一样("你是什么意思,有副作用?")。大多数情况下,它运行得很好。我们对2500万行的C系统进行了详细的副作用分析;

副作用分析仅在某些子表达式可能被求值不止一次时才有意义。OP的示例在注释中给出,不需要它们,并且可以通过以下规则处理:

rule not_on_disjunction(e1:condition, e2:condition):
    condition -> condition =
  " ! (e1 || e2) " ->  " !e1 && !e2";
rule double_not(e:condition):
    condition -> condition =
  " ! ! e " -->  " e ";

一个完整的,但简单的,有更详细描述的例子是这个对常规代数和微积分进行代数化简的例子。

对于特定的代码转换是否会使代码更具可读性,显然存在争议。恕我直言,这是因为代码的形状通常是一种艺术判断,而我们似乎都不同意艺术。这和让别人修改你的代码没有什么不同。