简体   繁体   中英

How to simplify this logical expression in a single return statement?

I have been trying to simplify this function in a single return A... B... C statement but some cases always slip out. How could this checks be expressed in a logical way (with and , or , not , etc.)?

bool f(bool C, bool B, bool A)
{
if (A)
    return true;

if (B)
    return false;
   
if (C)
    return true;

return false;
}
bool f(bool C, bool B, bool A)
{
if (A)
    return true;

if (B)
    return false;
   
if (C)
    return true;

return false;
}

is equivalent to

bool f(bool C, bool B, bool A)
{
if (A)
    return true;
else if (B)
    return false;
else if (C)
    return true;
else 
    return false;
}

is equivalent to:

bool f(bool C, bool B, bool A)
{
if (A)
    return true;
else if (!B)
{
    if (C)
        return true;
    else
        return false;
}
else
    return false;
}

is equivalent to:

bool f(bool C, bool B, bool A)
{
if (A)
    return true;
else if (!B and C)
    return true;
else
    return false;
}

is equivalent to:

bool f(bool C, bool B, bool A)
{
if (A or (!B and C))
    return true;
else
    return false;
}

is equivalent to:

bool f(bool C, bool B, bool A)
{
return (A or (!B and C));
}

If you write out all the combinations, you arrive at a table

在此处输入图像描述

static std::vector<bool> table{false, true, false, false, true, true, true, true};

which you can then extract the value with

table[4 * A + 2 * B + C]

Taking this one step further, you could simplify to

242 & (A << 4 + B << 2 + C)

Both are branchless, which is nice.

Make a truth table

  A    B     C     return
  1    0     0     1
  1    0     1     1
  1    1     0     1
  1    1     1     1
  0    0     0     0
  0    0     1     1
  0    1     0     0
  0    1     1     0

Then either you already see how it can be shortened or you just try something, for example:

bool f(bool C, bool B, bool A)
{
    return A || (C && not B);
}

Then write a test to see that it always returns the same. I wrote a "manual" test to see that the same truth table is produced:

void test(bool A, bool B, bool C){
    std::cout << A << " " << B << " " << C << " " << f(C,B,A) << "\n";
}

int main()
{
  for (int A = 1; A >= 0; --A){
      for (int B = 0; B<2;++B){
          for (int C = 0; C<2;++C){
              test(A,B,C);
          }
      }
  }
}

And indeed the output is :

1 0 0 1
1 0 1 1
1 1 0 1
1 1 1 1
0 0 0 0
0 0 1 1
0 1 0 0
0 1 1 0

TL;DR: When refactoring code tests are of major importance. Write the test make sure it passes, then refactor in tiny steps and make sure that each tiny refactoring still passed the test.

PS: Less code is not always simpler. Depending on what the conditions actually are (and I hope in your real code they have better names than just A , B and C ) your original code is potentially much better at expressing the intent of the code than something that uses less lines.

Alternative answer:

bool f(bool C, bool B, bool A)
{
if (A)
    return true;

if (B)
    return false;
   
if (C)
    return true;

return false;
}

You can create a boolean matrix for this:

A B C f(C, B, A)
f f f f
f f t t
f t f f
f t t f
t f f t
t f t t
t t f t
t t t t

The lower half evaluates to if(A) The remainding entries are these:

B C f(C, B, A)
f f f
f t t
t f f
t t f

There is only one true-case here: if(!B && C)

To combine these two statements you have to use or as only one of them needs to evaluate to true : if((A) || (!B && C))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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