簡體   English   中英

簡化重疊和條件語句

[英]Simplify overlapping And condition 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
        {
            ...
        }
    }
}

這個版本避免了任何重復和不必要的條件被評估,但是編寫起來非常冗長和痛苦。

因此,我希望有一種我不介意的更簡單但同樣有效的書寫方式……?

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

在這里,如果a為假,則對B()進行評估以進行下一個條件檢查。 在第二個條件中,如果b為假,則評估C()的下一個條件。 這樣,我們可以確保在需要時對每個函數進行評估。 但是對於最后一個條件,我們應該使用函數求值作為第二個操作數。

這非常類似於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(); });

在魔盒上直播


如果我們不能將不同的表達式放入同構類型中,則需要一個異構算法庫。 Boost Hana也許會工作。

您可以在if使用賦值表達式。

bool b,c;
if (((b = B()) && A()) || ((c = C()) && b) || (c && D()) )    {
    cout << "done";
}

正如@abdullah指出的那樣,必須注意由於快捷方式求值,當在||之后的條件中使用變量b可能未初始化變量b 因此,結果的表達式將在以后重用,必須位於&&運算符的左側,這可能會引入不必要的評估。

避免這種情況的一種方法是使用三態邏輯,其中變量“知道”是否已分配。 C ++沒有三態布爾值,但是可以通過數據類型int輕松模擬它:

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";
}

一種版本涉及將所有功能保存到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 });
}

用模板元編程解決問題!

//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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM