繁体   English   中英

C ++在if语句将条件设置为true与在循环中对条件进行或运算之间的行为差​​异

[英]C++ difference in behavior between if statement setting condition to true and or-ing a condition in loop

我当时正在为CS类开发Datalog解释器,但遇到一个奇怪的问题,即我的Rule评估花了太多时间才能完成。 看完我的代码后,我在下面进行了两项修改,以固定我的评估以正确的通过次数执行:

//original form
bool addedFacts = false;
for (X x: xs) {
    addedFacts = addedFacts || set.insert(x).second;
}
//modified form
bool addedFacts = false;
for (X x: xs) {
    if (set.insert(x).second) {
        addedFacts = true;
    }
}

对我来说,这两个代码结构在逻辑上是等效的。 是否存在一个原因,一个人执行正确,一个人执行错误/效率低下? 这是一个正在发生的问题的可构建示例:

#include <iostream>
#include <set>
#include <vector>

using std::set;
using std::vector;
using std::cout;
using std::endl;

const int CAP = 100;

class Rule {
public:
    int factor;
    Rule(int factor) {
        this->factor = factor;
    }
    bool evaluateInefficient(set<int>& facts) {
        vector<int> data;
        bool addedFacts = false;
        for (int fact : facts) {
            data.push_back(fact);
        }
        for (int datum : data) {
            int newFact = datum * factor;
            if (newFact < CAP) {
                addedFacts = addedFacts || facts.insert(newFact).second;
            }
        }
        return addedFacts;
    }
    bool evaluate(set<int>& facts) {
        vector<int> data;
        bool addedFacts = false;
        for (int fact : facts) {
            data.push_back(fact);
        }
        for (int datum : data) {
            int newFact = datum * factor;
            if (newFact < CAP) {
                if (facts.insert(newFact).second) {
                    addedFacts = true;
                }
            }
        }
        return addedFacts;
    }
};

int doublyInefficient(vector<Rule>& rules) {
    set<int> facts;
    facts.insert(1);
    bool addedFacts = true;
    int passes = 0;
    while (addedFacts) {
        passes++;
        addedFacts = false;
        for (Rule rule : rules) {
            addedFacts = addedFacts || rule.evaluateInefficient(facts);
        }
    }
    return passes;
}

int singlyInefficient(vector<Rule>& rules) {
    set<int> facts;
    facts.insert(1);
    bool addedFacts = true;
    int passes = 0;
    while (addedFacts) {
        passes++;
        addedFacts = false;
        for (Rule rule : rules) {
            addedFacts = addedFacts || rule.evaluate(facts);
        }
    }
    return passes;
}

int efficient(vector<Rule>& rules) {
    set<int> facts;
    facts.insert(1);
    bool addedFacts = true;
    int passes = 0;
    while (addedFacts) {
        passes++;
        addedFacts = false;
        for (Rule rule : rules) {
            if (rule.evaluate(facts)) {
                addedFacts = true;
            }
        }
    }
    return passes;
}

int main(int argc, char* argv[]) {
    //build the rules
    vector<Rule> rules;
    rules.push_back(Rule(2));
    rules.push_back(Rule(3));
    rules.push_back(Rule(5));
    rules.push_back(Rule(7));
    rules.push_back(Rule(11));
    rules.push_back(Rule(13));
    //Show three different codes that should (in my mind) take the same amount of passes over the rules but don't
    cout << "Facts populated after " << doublyInefficient(rules) << " passes through the Rules." << endl;
    cout << "Facts populated after " << singlyInefficient(rules) << " passes through the Rules." << endl;
    cout << "Facts populated after " << efficient(rules) << " passes through the Rules." << endl;
    getchar();
}

在Visual Studio 2017上以调试和发布模式(32位)运行时,我得到以下输出。据我所知,代码未进行优化。

Facts populated after 61 passes through the Rules.
Facts populated after 17 passes through the Rules.
Facts populated after 7 passes through the Rules.
addedFacts = addedFacts || set.insert(x).second;

if (set.insert(x).second) {
    addedFacts = true;
}

绝对不是同一回事。 第一段代码等效于:

if (!addedFacts) {
    addedFacts = set.insert(x).second;
}

!addedFacts检查有很大的不同。

由于短路评估而产生差异:考虑形式为(expr1 || expr2)的表达式。 短路意味着如果expr1计算为true ,则表达式expr2根本不会得到评估(参见此在线c ++标准草案 ,重点是我的):

5.15逻辑或运算符

|| 操作员组从左到右。 操作数都在上下文中转换为bool(Clause [conv])。 如果两个操作数中的任何一个为true,则返回true,否则返回false。 与|,||不同 保证从左到右的评估; 此外,如果第一个操作数的值为true,则不计算第二个操作数的值

因此,在您的表达式中添加了addedFacts || set.insert(x).second addedFacts || set.insert(x).second ,从addedFacts第一次变为true的点开始,将不再执行表达式set.insert(x).second 我想这是“错误”的行为,因为您的set将不包含相应的x

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM