質問

I've only been programming for a short while and have yet to find a way to write branching code that covers every possible configuration of two or more boolean values without any redundant if-statements.

For example, a table showing all possible outcomes may look like this:

          true    false
      ---------------------
true  | Thing A | Thing B |
      ---------------------
false | Thing C | Thing D |
      ---------------------

Yet the only code that I can think of to program things A though D is as follows:

if(boolean1) {
    if(boolean2)
        ThingA();
    else
        ThingB();
}
else if (boolean2)
    ThingC();
else
    ThingD();

Is there any way to write this without having to write a check for boolean2 twice? I am aware that only one check can run for one iteration of this code, but in a situation where there are more than two boolean values to worry about, this approach could become quite cumbersome.

役に立ちましたか?

解決 2

What you can do is to create a mask using bit-shifts and introduce a switch

#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <cstdint>

using namespace std;

int main() {
  bool bArray[2];
  for (size_t i = 0 ; i < 2 ; i++) {
    cin >> bArray[i];
  }

  uint32_t mask = 0;

  mask = bArray[0];
  mask = mask << 1;
  mask = mask ^ bArray[1];

  switch(mask) {
    case 0:
      cout << "bot false" << endl;
      break;
    case 1:
      cout << "first false, second true" << endl;
      break;
    case 2:
      cout << "first true, second false" << endl;
      break;
    case 3:
      cout << "both true" << endl;
      break;
  }
  return 0;
}

You can make it very elegant using the new C++11 extension, where you can overload literal operator ""_ and e.g. replace case 2: with "10"_bin2int in a similar way to how hashes are created in

Compile time string hashing

There is a full working example for such a switch here

http://dev.krzaq.cc/switch-on-strings-with-c11/

Using the information from above, the most elegant solution I can think of looks like this. Note how the literals are evaluated compile-time. You need a compiler which supports string-literals to compile this

#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <cstdint>

using namespace std;

constexpr uint32_t basis = 0;

constexpr uint32_t bin2int_compile_time(char const* str,
                                        uint32_t last_value = basis) {
  return *str ?
    bin2int_compile_time(str+1,(last_value << 1) ^ ((uint8_t)*str)-48) :
    last_value;
}

constexpr uint32_t operator "" _bin2int(char const* p, size_t) {
  return bin2int_compile_time(p);
}

uint32_t bool2int(const bool* bools, const size_t length) {
  uint32_t mask = bools[0];
  for (size_t i = 1 ; i < length ; i++) {
    mask = mask << 1;
    mask = mask ^ bools[i];
  }
  return mask;
}

int main() {
  const size_t nBooleans = 2;
  bool bArray[nBooleans];
  for (size_t i = 0 ; i < nBooleans ; i++) {
    cin >> bArray[i];
  }

  uint32_t mask = bool2int(bArray, nBooleans);

  switch(mask) {
    case "00"_bin2int:
      cout << "both false" << endl;
      break;
    case "01"_bin2int:
      cout << "first false, second true" << endl;
      break;
    case "10"_bin2int:
      cout << "first true, second false" << endl;
      break;
    case "11"_bin2int:
      cout << "both true" << endl;
      break;
    default:
      break;
  }
  return 0;
}

他のヒント

If your goal is to eliminate if statements, then a 4 entry lookup table of pointers to functions would be your most elegant answer; combine your two bits (booleans) into a single 2-bit value, and use that to determine which lookup table entry to use, i.e. in pseuodocode:

static FunctionArrayInYourLanguage fnAr[4] {ThingA(), ThingB(), ThingC(), ThingD()}

UnsignedIntegerType manyBooleans = (boolean1 LeftShift 1) BitwiseOR boolean2
Remark: Or any other way of combining your booleans, like casting a struct of 8 bits as a uint8_t in C or vice versa

Execute fnAr[manyBooleans]();

Note that this technique is extensible to many more than just 2 booleans if you can afford the lookup table space, and if you've really got a function for each possible combination, you can definitely afford the space.

There's the object oriented approach.

public interface Branch {
    public boolean isCondition();
    public void execute();
}

public class FirstThing implements Branch {

    private boolean boolean1;
    private boolean boolean2;

    public FirstThing(boolean boolean1, boolean boolean2) {
        this.boolean1 = boolean1;
        this.boolean2 = boolean2;
    }

    @Override
    public boolean isCondition() {
        return boolean1 & boolean2;
    }

    @Override
    public void execute() {
        thing00();      
    }

}

And so on for the rest of the table entries.

You add an instance of each of the implementation classes to a List. Loop through the list until isCondition() is true, then execute().

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top