ветвление на основе двух логических переменных - PullRequest
3 голосов
/ 05 августа 2011

Предположим, у меня есть две логические переменные, и я хочу делать совершенно разные вещи, основываясь на их значениях. Какой самый чистый способ достичь этого?

Вариант 1:

if (a && b)
{
    // ...
}
else if (a && !b)
{
    // ...
}
else if (!a && b)
{
    // ...
}
else
{
    // ...
}

Вариант 2:

if (a)
{
    if (b)
    {
        // ...
    }
    else
    {
        // ...
    }
}
else
{
    if (b)
    {
        // ...
    }
    else
    {
        // ...
    }
}

Вариант 3:

switch (a << 1 | b)
{
case 0:
    // ...
    break;

case 1:
    // ...
    break;

case 2:
    // ...
    break;

case 3:
    // ...
    break;
}

Вариант 4:

lut[a][b]();

void (*lut[2][2])() = {false_false, false_true, true_false, true_true};

void false_false()
{
    // ...
}

void false_true()
{
    // ...
}

void true_false()
{
    // ...
}

void true_true()
{
    // ...
}

Варианты 3 и 4 слишком сложны / сложны для среднего программиста? Любые другие варианты, которые я пропустил?

Ответы [ 4 ]

13 голосов
/ 05 августа 2011

Первый вариант самый понятный и читабельный, но его можно настроить:

if (a && b) {
    // ...
} else if (a) { // no need to test !b here - b==true would be the first case
    // ...
} else if (b) { //no need to test !a here - that would be the first case
    // ...
} else { // !a&&!b - the last remaining
    // ...
}
4 голосов
/ 05 августа 2011

Вы забыли о:

if (a) a_true(b);
else a_false(b);

, который, вероятно, является лучшим выбором, когда это применимо и когда вам действительно нужно 4 различных поведения.это как запах кода, если у меня есть 2 ^ n различных поведений, которые не очень хорошо разлагаются, как указано выше.Тогда я могу подумать о выполнении:

enum { case1, case2, ... }

int dispatch_cases(bool a, bool b, bool c, ..., bool z);

switch (dispatch_cases(a, b, ..., z))
{
case case1:
    ...
};

, но без контекста трудно сказать, необходима ли такая сложность.

1 голос
/ 05 августа 2011

Только для двух логических значений, любое из них является хорошим и разумным.Можно выбирать на основе его вкуса.

Однако, если существует более двух логических значений, скажем four booleans, то я лично выбрал бы таблицу поиска, и я сделал бы это следующим образом

typedef void (*functype)();

//16 functions to handle 16 cases!
void f0() {}
void f1() {}
//...so on
void f15() {}

//setup lookup table
functype lut[] = 
{
    f0,   //0000 - means all bool are false
    f1,   //0001
    f2,   //0010
    f3,   //0011
    f4,   //0100
    f5,   //0101
    f6,   //0110
    f7,   //0111
    f8,   //1000
    f9,   //1001
    f10,  //1010
    f11,  //1011
    f12,  //1100
    f13,  //1101
    f14,  //1110
    f15   //1111 - means all bool are true
};

lut[MakeInt(b1,b2,b3,b4)](); //call

MakeInt() легко написать:

int MakeInt(bool b1, bool b2, bool b3, bool b4)
{
   return b1 | (b2<<1) | (b3 <<2) | (b4<<3);
}
1 голос
/ 05 августа 2011

ИМХО, я пойду за variant 3. Потому что лично мне не нравится if/else, когда я проверяю равенство. В нем четко говорится, что есть только 4 возможности.

Одна незначительная правка будет:

inline int STATES(int X, int Y) { return (X<<1) | Y; }
// ...
switch (STATES(a,b))

Чтобы сделать его более необычным, вы можете заменить 0,1,2,3 на enum.

enum States {
  NONE,
  ONLY_B.
  ONLY_A,
  BOTH
}; 
...