简化重叠和条件语句

时间:2017-12-08 19:12:53

标签: c++ conditional-statements

我试图找出简化以下形式的条件语句的最佳方法:

if( ( A() && B() ) || ( B() && C() ) || ( C() && D() ) || .... )
{
    code
}

A / B / C / D / etc等功能相对较贵,因此每次调用两次都不理想。

我想到的两种选择:

bool a = A();
bool b = B();
bool c = C();
bool d = D();
if( ( a && b ) || ( b && c ) || ( c && d ) || .... )
{
    code
}

此版本并不理想,因为即使A()和B()都为真,每次都会评估C(),D()和任何其他条件。在原始版本中,在这种情况下,它们根本就不会被调用。

bool b = B();
if( ( A() && b ) )
{
    code
}
else
{
    bool c = C();
    if( ( b && c ) )
    {
        code
    }
    else
    {
        bool d = D();
        if( ( c && D ) )
        {
            code
        }
        else
        {
            ...
        }
    }
}

这个版本避免了任何重复和不必要的条件被评估,但写作非常冗长和痛苦。

所以我希望有一些更简单但同样有效的方式来编写它,我没想到......?

5 个答案:

答案 0 :(得分:3)

bool a = A(), b, c, d;
if (((b = B()) && a) || ((c = C()) && b) || (d = D() && c) || ...) {
    // code;
}

如果a为false,则评估B()以进行下一次条件检查。在第二个条件中,如果b为false,则会评估C()的下一个条件。通过这种方式,我们可以确保在需要时评估每个功能。但是对于最后一个条件,我们应该使用函数求值作为第二个操作数。

答案 1 :(得分:2)

这与adjacent_find非常相似,但使用adjacent_find则需要重新评估AB等。我们可以编写我们自己的版本,通过自定义谓词来处理这个问题,而不是“比较相等”:

template <typename FwdIter, typename Pred>
FwdIter find_if_consecutive(FwdIter cur, FwdIter last, Pred pred) {
    if (cur == last) return last;

    bool curMatches = false, nextMatches = pred(*cur);

    for (auto next = std::next(cur); next != last; ++cur, ++next) {
        curMatches = std::exchange(nextMatches, pred(*next));
        if (curMatches && nextMatches) return cur;
        // Note: this *might* possibly be faster by moving
        // forward by 2 when `nextMatches` is false, which
        // avoids one of the `curMatches && nextMatches`.
        // Implementation left as an exercise for the reader.
    }

    return last;
}

如果ABC,...都可以是相同类型的函数指针,则可以这样使用:

auto fns = { A, B, C, D }; // Create an initializer_list

return fns.end()
    != find_if_consecutive(fns.begin(), fns.end(), [](auto f) { return f(); });

Live on Wandbox

如果我们不能将不同的表达式放入同类型中,我们需要一个异构算法库;也许Boost Hana会工作。

答案 2 :(得分:1)

您可以在if

中使用赋值表达式
bool b,c;
if (((b = B()) && A()) || ((c = C()) && b) || (c && D()) )    {
    cout << "done";
}

正如@abdullah指出的那样,由于快捷方式评估,在b之后的条件中使用变量||时,可能不会初始化变量&&。因此,结果将在以后重用的表达式必须位于int - 运算符的左侧,这可能会引入不必要的求值。

避免这种情况的一种方法是使用三态逻辑,其中变量“知道”它是否已被分配。 C ++没有三态布尔值,但可以通过数据类型int a = A(); int b=-1; int c=-1; int d=-1; if( ( a && (b=B()) ) || ( (b<0?b=B():b) && (c=C()) ) || ( (c<0?c=C():c) && (d=D()) ) ) { cout << "done"; } 轻松模拟:

{{1}}

答案 3 :(得分:0)

一个版本涉及将所有函数保存到std::vector<std::function<bool()>>,这会创建非常简单的调用约定并简化逻辑:

#include<vector>
#include<functional>
#include<iostream>

bool evaluate_vec(std::vector<std::function<bool()>> const& funcs) {
    bool a, b;
    for (size_t index = 0; index < funcs.size(); index++) {
        //We'll ping-pong between the two cached evaluations of the variables
        if ((index & 1) == 0)
            a = funcs[index]();
        else
            b = funcs[index]();
        if (index > 0)
            //The short-circuiting behavior we intend to have
            if (a && b)
                return true;
    }
    return false;
}

int main() {
    bool evaluation = evaluate_vec({ A, B, C, D });
}

答案 4 :(得分:0)

使用模板元编程解决问题!

//If we run out of functions and didn't find a pair that both returned true, we return false
bool evaluate_impl(bool) {
    return false;
}

//At this point, we're guaranteed to have at least 1 cached result and 1 unevaluated function call.
template<typename Func, typename ... Funcs>
bool evaluate_impl(bool cached, Func && func, Funcs&& ... funcs) {
    //store the result of the function
    bool result = func();
    if (cached && result)
        return true;
    else {
        //the result of this call becomes the new cached value
        return evaluate_impl(result, std::forward<Funcs>(funcs)...);
    }
}

//We receive all the functions
template<typename Func, typename ... Funcs>
bool evaluate_tmp(Func && func, Funcs&& ... funcs) {
    //We cache the result of calling the first function and pass along to the next step
    return evaluate_impl(func(), std::forward<Funcs>(funcs)...);
}

int main() {
    bool result = evaluate_tmp(A, B, C, D);
}

这有效地扩展以支持任意数量的函数调用(无论如何,都是编译器允许的)。

int main() {
    bool result = evaluate_tmp(A, B, A, C, A, A, A, A, D, A, B, A, C, A, D, A);
}