[英]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
將需要重新評估A
, B
有時,等等。 我們可以編寫自己的版本來處理此問題,方法是在謂詞上進行自定義,而不是“比較相等”:
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;
}
如果A
, B
, C
,...可以都是相同類型的函數指針,則可以這樣使用:
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.