Как улучшить логику, чтобы проверить, соответствуют ли 4 логическим значениям некоторые случаи - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть четыре bool значения:

bool bValue1;
bool bValue2;
bool bValue3;
bool bValue4;

Допустимые значения:

         Scenario 1 | Scenario 2 | Scenario 3
bValue1: true       | true       | true
bValue2: true       | true       | false
bValue3: true       | true       | false
bValue4: true       | false      | false

Так, например, этот сценарий неприемлем:

bValue1: false
bValue2: true
bValue3: true
bValue4: true

В настоящий момент я пришел с этим оператором if для обнаружения плохих сценариев:

if(((bValue4 && (!bValue3 || !bValue2 || !bValue1)) ||
   ((bValue3 && (!bValue2 || !bValue1)) ||
   (bValue2 && !bValue1) ||
   (!bValue1 && !bValue2 && !bValue3 && !bValue4))
{
    // There is some error
}

Можно ли улучшить / упростить эту логику оператора?

Ответы [ 30 ]

0 голосов
/ 03 декабря 2018

Я обозначаю a, b, c, d для ясности, и A, B, C, D для дополнений

bValue1 = a (!A)
bValue2 = b (!B)
bValue3 = c (!C)
bValue4 = d (!D)

Уравнение

1 = abcd + abcD + aBCD
  = a (bcd + bcD + BCD)
  = a (bc + BCD)
  = a (bcd + D (b ^C))

Используйте любые уравнения, которые подходятвы.

0 голосов
/ 03 декабря 2018

Вот упрощенная версия:

if (bValue1&&(bValue2==bValue3)&&(bValue2||!bValue4)) {
    // acceptable
} else {
    // not acceptable
}

Обратите внимание, конечно, что это решение более запутанно, чем оригинальное, его значение может быть сложнее понять.


Обновление: MSalters в комментариях нашел еще более простое выражение:

if (bValue1&&(bValue2==bValue3)&&(bValue2>=bValue4)) ...
0 голосов
/ 03 декабря 2018

Мы можем использовать карту Карно и сократить ваши сценарии до логического уравнения.Я использовал Онлайн-карту Карно решатель со схемой для 4 переменных.

enter image description here

Это дает:

enter image description here

Изменение A, B, C, D на bValue1, bValue2, bValue3, bValue4, это не что иное, как:

bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4

Итак, ваше if утверждениестановится:

if(!(bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4))
{
    // There is some error
}
  • Карты Карно особенно полезны, когда у вас много переменных и много условий, которые должны оцениваться true.
  • После сокращения сценариев true до логического уравнения добавление соответствующих комментариев, указывающих сценарии true, является хорошей практикой.
0 голосов
/ 03 декабря 2018

Реальный вопрос здесь заключается в следующем: что происходит, когда другой разработчик (или даже автор) должен изменить этот код несколько месяцев спустя.

Я бы предложил смоделировать это как битовые флаги:

const int SCENARIO_1 = 0x0F; // 0b1111 if using c++14
const int SCENARIO_2 = 0x0E; // 0b1110
const int SCENARIO_3 = 0x08; // 0b1000

bool bValue1 = true;
bool bValue2 = false;
bool bValue3 = false;
bool bValue4 = false;

// boolean -> int conversion is covered by standard and produces 0/1
int scenario = bValue1 << 3 | bValue2 << 2 | bValue3 << 1 | bValue4;
bool match = scenario == SCENARIO_1 || scenario == SCENARIO_2 || scenario == SCENARIO_3;
std::cout << (match ? "ok" : "error");

Если существует гораздо больше сценариев или больше флагов, табличный подход является более читабельным и расширяемым, чем использование флагов.Для поддержки нового сценария требуется еще одна строка в таблице.

int scenarios[3][4] = {
    {true, true, true, true},
    {true, true, true, false},
    {true, false, false, false},
};

int main()
{
  bool bValue1 = true;
  bool bValue2 = false;
  bool bValue3 = true;
  bool bValue4 = true;
  bool match = false;

  // depending on compiler, prefer std::size()/_countof instead of magic value of 4
  for (int i = 0; i < 4 && !match; ++i) {
    auto current = scenarios[i];
    match = bValue1 == current[0] && 
            bValue2 == current[1] && 
            bValue3 == current[2] && 
            bValue4 == current[3];
  }

  std::cout << (match ? "ok" : "error");
}
0 голосов
/ 04 декабря 2018

Каждый ответ слишком сложен и труден для чтения.Лучшее решение для этого - заявление switch().Он удобен для чтения и упрощает добавление / изменение дополнительных случаев.Компиляторы также способны оптимизировать switch() операторы.

switch( (bValue4 << 3) | (bValue3 << 2) | (bValue2 << 1) | (bValue1) )
{
    case 0b1111:
        // scenario 1
        break;

    case 0b0111:
        // scenario 2
        break;

    case 0b0001:
        // scenario 3
        break;

    default:
        // fault condition
        break;
}

Конечно, вы можете использовать константы и ИЛИ вместе в операторах case для еще большей читаемости.

0 голосов
/ 03 декабря 2018

Я бы также использовал переменные быстрого доступа для ясности.Как отмечалось ранее, сценарий 1 соответствует сценарию 2, поскольку значение bValue4 не влияет на истинность этих двух сценариев.

bool MAJORLY_TRUE=bValue1 && bValue2 && bValue3
bool MAJORLY_FALSE=!(bValue2 || bValue3 || bValue4)

, тогда ваше выражение становится:

if (MAJORLY_TRUE || (bValue1 && MAJORLY_FALSE))
{
     // do something
}
else
{
    // There is some error
}

ДачаЗначимые имена для переменных MAJORTRUE и MAJORFALSE (а также фактически для bValue * vars) очень помогли бы с удобочитаемостью и обслуживанием.

0 голосов
/ 04 декабря 2018

Я бы стремился к простоте и удобочитаемости.

bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
bool scenario2 = bValue1 && bValue2 && bValue3 && !bValue4;
bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4;

if (scenario1 || scenario2 || scenario3) {
    // Do whatever.
}

Обязательно замените имена сценариев, а также имена флагов чем-то описательным.Если это имеет смысл для вашей конкретной проблемы, вы можете рассмотреть эту альтернативу:

bool scenario1or2 = bValue1 && bValue2 && bValue3;
bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4;

if (scenario1or2 || scenario3) {
    // Do whatever.
}

Здесь важна не логика предикатов.Он описывает ваш домен и четко выражает ваши намерения.Главное здесь - дать всем входным и промежуточным переменным хорошие имена.Если вы не можете найти правильные имена переменных, это может быть признаком того, что вы неправильно описываете проблему.

0 голосов
/ 03 декабря 2018

Сосредоточьтесь на удобочитаемости проблемы, а не на конкретном операторе «если».

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

Разделив вещи на классы (не стесняйтесь просто использовать функции или любой другой инструмент, который вы предпочитаете) с понятными именами -мы можем намного проще показать значения каждого сценария.Что еще более важно, в системе с множеством движущихся частей - проще поддерживать и присоединяться к существующим системам (опять же, несмотря на то, сколько задействовано дополнительного кода).

#include <iostream>
#include <vector>
using namespace std;

// These values would likely not come from a single struct in real life
// Instead, they may be references to other booleans in other systems
struct Values
{
    bool bValue1; // These would be given better names in reality
    bool bValue2; // e.g. bDidTheCarCatchFire
    bool bValue3; // and bDidTheWindshieldFallOff
    bool bValue4;
};

class Scenario
{
public:
    Scenario(Values& values)
    : mValues(values) {}

    virtual operator bool() = 0;

protected:
    Values& mValues;    
};

// Names as examples of things that describe your "scenarios" more effectively
class Scenario1_TheCarWasNotDamagedAtAll : public Scenario
{
public:
    Scenario1_TheCarWasNotDamagedAtAll(Values& values) : Scenario(values) {}

    virtual operator bool()
    {
        return mValues.bValue1
        && mValues.bValue2
        && mValues.bValue3
        && mValues.bValue4;
    }
};

class Scenario2_TheCarBreaksDownButDidntGoOnFire : public Scenario
{
public:
    Scenario2_TheCarBreaksDownButDidntGoOnFire(Values& values) : Scenario(values) {}

    virtual operator bool()
    {
        return mValues.bValue1
        && mValues.bValue2
        && mValues.bValue3
        && !mValues.bValue4;
    }   
};

class Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere : public Scenario
{
public:
    Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere(Values& values) : Scenario(values) {}

    virtual operator bool()
    {
        return mValues.bValue1
        && !mValues.bValue2
        && !mValues.bValue3
        && !mValues.bValue4;
    }   
};

Scenario* findMatchingScenario(std::vector<Scenario*>& scenarios)
{
    for(std::vector<Scenario*>::iterator it = scenarios.begin(); it != scenarios.end(); it++)
    {
        if (**it)
        {
            return *it;
        }
    }
    return NULL;
}

int main() {
    Values values = {true, true, true, true};
    std::vector<Scenario*> scenarios = {
        new Scenario1_TheCarWasNotDamagedAtAll(values),
        new Scenario2_TheCarBreaksDownButDidntGoOnFire(values),
        new Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere(values)
    };

    Scenario* matchingScenario = findMatchingScenario(scenarios);

    if(matchingScenario)
    {
        std::cout << matchingScenario << " was a match" << std::endl;
    }
    else
    {
        std::cout << "No match" << std::endl;
    }

    // your code goes here
    return 0;
}
0 голосов
/ 04 декабря 2018

Простой подход - найти ответ, который вы считаете приемлемым.

Да = (boolean1 && boolean2 && boolean3 && boolean4) + + ...

Теперь, если возможно, упростите уравнениеиспользуя булеву алгебру.

, как в этом случае, приемлемые1 и 2 объединяются в (boolean1 && boolean2 && boolean3).

Следовательно, окончательный ответ:

(boolean1 && boolean2 && boolean3) || 
((boolean1 && !boolean2 && !boolean3 && !boolean4)
0 голосов
/ 03 декабря 2018

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

В конце я решил добавить три новых "сценария" boolean метода:

bool CChristianLifeMinistryValidationDlg::IsFirstWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
    return (INCLUDE_ITEM1(pEntry) && 
           !INCLUDE_ITEM2(pEntry) && 
           !INCLUDE_ITEM3(pEntry) && 
           !INCLUDE_ITEM4(pEntry));
}

bool CChristianLifeMinistryValidationDlg::IsSecondWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
    return (INCLUDE_ITEM1(pEntry) &&
            INCLUDE_ITEM2(pEntry) &&
            INCLUDE_ITEM3(pEntry) &&
            INCLUDE_ITEM4(pEntry));
}

bool CChristianLifeMinistryValidationDlg::IsOtherWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
    return (INCLUDE_ITEM1(pEntry) && 
            INCLUDE_ITEM2(pEntry) && 
            INCLUDE_ITEM3(pEntry) && 
           !INCLUDE_ITEM4(pEntry));
}

Затем я смог применить те, которые мои моиПроцедура проверки выглядит следующим образом:

if (!IsFirstWeekStudentItems(pEntry) && !IsSecondWeekStudentItems(pEntry) && !IsOtherWeekStudentItems(pEntry))
{
    ; Error
}

В моем приложении в реальном времени 4 значения bool на самом деле извлекаются из DWORD, в который записано 4 значения.

Еще раз спасибо всем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...