У меня четыре 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
}
Можно ли улучшить / упростить логику этого оператора?
c++
if-statement
Эндрю Тракл
источник
источник
if
оператора. Кроме того, поскольку это логические флаги, вы можете моделировать каждый сценарий как константу и проверять его.if (!((bValue1 && bValue2 && bValue3) || (bValue1 && !bValue2 && !bValue3 && !bValue4)))
bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
Ответы:
Я бы стремился к удобочитаемости: у вас всего 3 сценария, разберитесь с ними с 3 отдельными if:
Легко читать и отлаживать, ИМХО. Кроме того, вы можете присвоить переменную
whichScenario
, продолжая работу сif
.Имея всего 3 сценария, я бы не пошел с чем-то вроде «если первые 3 значения верны, я могу избежать проверки четвертого значения»: это затруднит чтение и сопровождение вашего кода.
Не изящное решение
может бытьконечно, но в этом случае нормально: легко и читабельно.Если ваша логика усложняется, выбросьте этот код и подумайте об использовании чего-нибудь еще для хранения различных доступных сценариев (как предлагает Зладек).
Мне очень нравится первое предложение, данное в этом ответе : легко читается, не подвержено ошибкам, легко обслуживается
(Почти) не по теме:
Я не пишу много ответов здесь, в StackOverflow. Это действительно забавно, что принятый выше ответ, безусловно, является самым ценным ответом в моей истории (я никогда не получал больше 5-10 голосов), хотя на самом деле это не то, что я обычно считаю «правильным» способом сделать это.
Но простота часто является «правильным способом сделать это», многие люди, кажется, думают так, и я должен думать об этом больше, чем я :)
источник
valid
и разделив их с помощью||
вместо измененияvalid
в отдельных блоках операторов. Я не могу привести пример в комментарии, но вы можете вертикально выровнять||
операторы по левому краю, чтобы сделать это очень понятным; отдельные условия уже заключены в круглые скобки столько, сколько они должны быть (дляif
), поэтому вам не нужно добавлять какие-либо символы в выражения, кроме того, что уже есть.if($bValue1)
поскольку это всегда должно быть правдой, технически допускающее небольшое улучшение производительности (хотя здесь мы говорим о незначительных суммах).bValue4
Я бы стремился к простоте и удобочитаемости.
Обязательно замените названия сценариев, а также названия флагов чем-нибудь описательным. Если это имеет смысл для вашей конкретной проблемы, вы можете рассмотреть эту альтернативу:
Здесь важна не логика предикатов. Он описывает ваш домен и ясно выражает ваши намерения. Ключевым моментом здесь является дать хорошие имена всем входным параметрам и промежуточным переменным. Если вы не можете найти подходящие имена переменных, это может быть признаком того, что вы неправильно описываете проблему.
источник
Мы можем использовать карту Карно и свести ваши сценарии к логическому уравнению. Я использовал решатель карт Карно Online со схемой для 4 переменных.
Это дает:
Переходя
A, B, C, D
наbValue1, bValue2, bValue3, bValue4
, это не что иное, как:Итак, ваше
if
утверждение становится:true
.true
сценариев к логическому уравнению добавление соответствующих комментариев, указывающих наtrue
сценарии, является хорошей практикой.источник
//!(ABC + AB'C'D') (By K-Map logic)
, Это было бы хорошее время для разработчика изучить K-Maps, если он еще не знает их.E
иF
условия , и 4 новых сценариев? Сколько времени нужно, чтобыif
правильно обновить эту инструкцию? Как проверка кода проверяет, все ли в порядке? Проблема не в технической стороне, а в «деловой» стороне.A
:ABC + AB'C'D' = A(BC + B'C'D')
(это можно даже учесть,A(B ^ C)'(C + D')
хотя я бы осторожно назвал это «упрощением»).Настоящий вопрос здесь: что произойдет, если другой разработчик (или даже автор) должен изменить этот код несколько месяцев спустя.
Я бы предложил моделировать это как битовые флаги:
Если существует намного больше сценариев или больше флагов, табличный подход более читабелен и расширяем, чем использование флагов. Для поддержки нового сценария требуется просто еще одна строка в таблице.
источник
SCENARIO_2 = true << 3 | true << 2 | true << 1 | false;
2: избегать переменных SCENARIO_X, а затем сохранить все доступные сценарии в файле<std::set<int>
. Добавление сценария будет чем-то вродеmySet.insert( true << 3 | false << 2 | true << 1 | false;
небольшого перегиба для всего 3 сценария, OP принял быстрое, грязное и простое решение, которое я предложил в своем ответе.std::find
?).scenario
значения кажутся мне излишне подверженными ошибкам.Мой предыдущий ответ уже является принятым ответом, я добавляю здесь кое-что, что я считаю читабельным, простым и в этом случае открытым для будущих изменений:
Начиная с ответа @ZdeslavVojkovic (который я считаю неплохим), я придумал следующее:
Смотрите его на работе здесь
Что ж, это «элегантное и удобное в обслуживании» (IMHO) решение, к которому я обычно стремлюсь, но на самом деле, для случая OP мой предыдущий ответ «если» лучше соответствует требованиям OP, даже если он не изящный и не обслуживаемый.
источник
Я также хотел бы предложить другой подход.
Моя идея состоит в том, чтобы преобразовать bools в целое число, а затем сравнить, используя вариативные шаблоны:
Обратите внимание, как эта система может поддерживать до 32 логических значений в качестве входных данных. заменяя
unsigned
сunsigned long long
(илиuint64_t
) увеличивает поддержку 64 случаев. Если вам не нравитсяif (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u)
, вы также можете использовать еще один метод вариативного шаблона:источник
bitmap_from_bools
, илиbools_to_bitmap
?bools_to_unsigned
. Растровое изображение - хорошее ключевое слово; изм.summary!= 0b1111u &&...
.a != b || a != c
всегда верно, еслиb != c
Вот упрощенная версия:
Обратите внимание, конечно, это решение более запутанное, чем исходное, его смысл может быть труднее понять.
Обновление: MSalters в комментариях нашел еще более простое выражение:
источник
simple
действительно расплывчатый термин. Многие люди понимают это в этом контексте как более простое для понимания разработчиком, а не компилятором для генерации кода, поэтому более подробное может быть проще.Подумайте о прямом переводе таблиц в вашу программу. Управляйте программой, основываясь на таблице, вместо того, чтобы имитировать ее с помощью логики.
сейчас
это, насколько возможно, напрямую кодирует вашу таблицу истинности в компилятор.
Живой пример .
Вы также можете использовать
std::any_of
напрямую:компилятор может встроить код, исключить любую итерацию и построить для вас собственную логику. Между тем, ваш код точно отражает ваше понимание проблемы.
источник
Я даю здесь только свой ответ, поскольку в комментариях кто-то предложил показать мое решение. Я хочу поблагодарить всех за их понимание.
В конце концов, я решил добавить три новых "сценарных"
boolean
метода:Затем я смог применить эти мои процедуры проверки следующим образом:
В моем живом приложении 4 значения bool фактически извлекаются из a, в
DWORD
котором закодированы 4 значения.Еще раз спасибо всем.
источник
INCLUDE_ITEM1
т. Д. Лучше, и у вас все в порядке. :)Я не вижу никаких ответов, в которых говорится, что нужно назвать сценарии, хотя решение OP делает именно это.
Для меня лучше всего инкапсулировать комментарий каждого сценария либо в имя переменной, либо в имя функции. Вы с большей вероятностью проигнорируете комментарий, чем имя, и если ваша логика изменится в будущем, вы с большей вероятностью измените имя, чем комментарий. Вы не можете реорганизовать комментарий.
Если вы планируете повторно использовать эти сценарии вне своей функции (или можете захотеть это сделать), создайте функцию, которая сообщает, что она оценивает (
constexpr
/noexcept
необязательно, но рекомендуется):Если возможно, сделайте эти методы класса (как в решении OP). Вы можете использовать переменные внутри своей функции, если не думаете, что будете повторно использовать логику:
Компилятор, скорее всего, решит, что если bValue1 ложно, то все сценарии ложны. Не беспокойтесь о том, чтобы сделать его быстрым, просто правильным и читаемым. Если вы профилируете свой код и обнаруживаете, что это узкое место, потому что компилятор сгенерировал неоптимальный код с -O2 или выше, попробуйте его переписать.
источник
AC / C ++ способ
Этот подход является масштабируемым, так как если количество допустимых условий растет, вы легко просто добавляете их в список сценариев.
источник
true
. Компилятор, который использует «все, что ненулевое, истинно», вызывает сбой этого кода. Обратите внимание, чтоtrue
необходимо преобразовать в1
, его просто не нужно хранить как таковой.2 is not equal to true but evaluates to true
, мой код неint 1 = true
работает и работает, пока все true преобразуются в одно и то же значение int, поэтому вот мой вопрос: почему компилятор должен действовать случайным образом при преобразовании верен базовому типу int. Не могли бы вы подробнее рассказать?memcmp
для проверки логических условий - это не способ C ++, и я также сомневаюсь, что это установленный способ C.Легко заметить, что первые два сценария похожи - они разделяют большинство условий. Если вы хотите выбрать, в каком сценарии вы находитесь в данный момент, вы можете написать это так (это модифицированное решение @ gian-paolo ):
Двигаясь дальше, вы можете заметить, что первое логическое значение всегда должно быть истинным, что является условием входа, поэтому вы можете получить:
Более того, теперь вы можете ясно видеть, что bValue2 и bValue3 в некоторой степени связаны - вы можете извлечь их состояние в некоторые внешние функции или переменные с более подходящим именем (хотя это не всегда легко или уместно):
У этого способа есть некоторые преимущества и недостатки:
Если вы прогнозируете, что в приведенную выше логику произойдут изменения, вам следует использовать более простой подход, представленный @ gian-paolo .
В противном случае, если эти условия четко установлены и представляют собой своего рода «твердые правила», которые никогда не изменятся, рассмотрим мой последний фрагмент кода.
источник
Как предлагает mch, вы можете:
где первая строка охватывает два первых хороших случая, а вторая строка - последний.
Live Demo, где я поигрался и он твои дела проходит.
источник
Небольшая вариация прекрасного ответа @GianPaolo, который некоторым может быть легче прочитать:
источник
Каждый ответ слишком сложен и труден для чтения. Лучшее решение этого -
switch()
заявление. Он удобен для чтения и упрощает добавление / изменение дополнительных случаев. Компиляторы тоже хороши в оптимизацииswitch()
операторов.Вы, конечно, можете использовать константы и OR их вместе в
case
операторах для еще большей читабельности.источник
Я бы также использовал быстрые переменные для ясности. Как отмечалось ранее, сценарий 1 равен сценарию 2, поскольку значение bValue4 не влияет на истинность этих двух сценариев.
тогда ваше выражение становится:
Присвоение значимых имен переменным MAJORTRUE и MAJORFALSE (а также фактически переменным bValue *) очень поможет с удобочитаемостью и обслуживанием.
источник
Сосредоточьтесь на удобочитаемости проблемы, а не на конкретном «если».
Хотя это приведет к появлению большего количества строк кода, и некоторые могут посчитать это либо излишним, либо ненужным. Я бы предположил, что абстрагирование ваших сценариев от конкретных логических значений - лучший способ сохранить читаемость.
Разделив вещи на классы (не стесняйтесь просто использовать функции или любой другой инструмент, который вы предпочитаете) с понятными именами, мы сможем гораздо легче показать смысл каждого сценария. Что еще более важно, в системе со многими движущимися частями ее легче поддерживать и присоединять к существующим системам (опять же, несмотря на то, сколько дополнительного кода задействовано).
источник
Это зависит от того, что они представляют.
Например, если 1 - это ключ, а 2 и 3 - два человека, которые должны согласиться (кроме тех случаев, когда они согласны,
NOT
им нужен третий человек - 4 - для подтверждения), наиболее читаемым может быть:по многочисленным просьбам:
источник
bValue
.Побитовая операция выглядит очень чисто и понятно.
источник
Я обозначаю a, b, c, d для ясности и A, B, C, D для дополнений.
Уравнение
Используйте любые уравнения, которые вам подходят.
источник
просто
источник
Просто личное предпочтение перед принятым ответом, но я бы написал:
источник
Во-первых, предполагая, что вы можете изменить только проверку сценария, я бы сосредоточился на удобочитаемости и просто заключил проверку в функцию, чтобы вы могли просто позвонить
if(ScenarioA())
.Теперь, предполагая, что вы действительно хотите / должны оптимизировать это, я бы рекомендовал преобразовать тесно связанные логические значения в постоянные целые числа и использовать для них битовые операторы.
Это делает выражение сценария таким же простым, как перечисление того, что является его частью, позволяет использовать оператор switch для перехода к нужному условию и сбивать с толку коллег-разработчиков, которые не видели этого раньше. (C # RegexOptions использует этот шаблон для установки флагов, я не знаю, есть ли пример библиотеки C ++)
источник
if
Некоторым людям будет легче читать вложенные s. Вот моя версияисточник
bValue1
блок, вы можете рассматривать все в нем как новую свежую страницу в своем умственном процессе. Бьюсь об заклад, подход к проблеме может быть очень личным или даже культурным.На этот вопрос было дано несколько правильных ответов, но я бы придерживался другой точки зрения: если код выглядит слишком сложным, что-то не так. . Код будет сложно отлаживать, и, скорее всего, он будет «одноразовым».
В реальной жизни, когда мы находим такую ситуацию:
Когда четыре состояния связаны таким точным шаблоном, мы имеем дело с конфигурацией некой «сущности» в нашей модели. .
Крайняя метафора - это то, как мы описали бы «людей» в модели, если бы мы не знали об их существовании как единых сущностях с компонентами, соединенными в определенные степени свободы: нам пришлось бы описывать независимые состояния «туловища», «руки», «ноги» и «голова», которые усложнили бы понимание описанной системы. Непосредственным результатом были бы неестественно сложные логические выражения.
Очевидно, что способ уменьшить сложность - это абстракция, а в C ++ предпочтительным инструментом является объектная парадигма .
Возникает вопрос: почему существует такая закономерность? Что это такое и что из себя представляет?
Поскольку мы не знаем ответа, мы можем прибегнуть к математической абстракции: массиву : у нас есть три сценария, каждый из которых теперь является массивом.
На этом этапе у вас есть исходная конфигурация. как массив. Например,
std::array
есть оператор равенства:В этот момент ваш синтаксис становится:
Как и ответ Джан Паоло, он короткий, ясный и легко проверяемый / отлаживаемый. В этом случае мы делегировали детали булевых выражений компилятору.
источник
Вам не придется беспокоиться о недопустимых комбинациях логических флагов, если вы избавитесь от логических флагов.
У вас явно есть три состояния (сценария). Было бы лучше смоделировать это и вывести логические свойства из этих состояний, а не наоборот.
Это определенно больше кода, чем в ответе Джан Паоло , но в зависимости от вашей ситуации это может быть гораздо более удобным в обслуживании:
enum
случаях вswitch
операторах перехватит средства получения свойств, которые не обрабатывают этот сценарий.У этого подхода также есть побочное преимущество - он очень эффективен.
источник
Мои 2 цента: объявить переменную сумму (целое число), чтобы
Сравните сумму с желаемыми условиями и все. Таким образом, вы можете легко добавить больше условий в будущем, чтобы его было легко читать.
источник
Принятый ответ хорош, если у вас всего 3 случая и логика для каждого проста.
Но если логика для каждого случая была более сложной или случаев намного больше, гораздо лучшим вариантом было бы использовать шаблон проектирования цепочки ответственности .
Вы создаете,
BaseValidator
который содержит ссылку на,BaseValidator
а также методvalidate
и метод для вызова проверки на указанном валидаторе.Затем вы создаете несколько подклассов, которые наследуются от метода
BaseValidator
, переопределяяvalidate
его логикой, необходимой для каждого валидатора.Затем использовать его просто: создайте экземпляр каждого из ваших валидаторов и установите каждый из них в качестве корня для других:
По сути, каждый случай проверки имеет свой собственный класс, который отвечает за (а) определение того, соответствует ли проверка этому случаю, и (б) отправка валидации кому-то еще в цепочке, если это не так.
Обратите внимание, что я не знаком с C ++. Я попытался сопоставить синтаксис с некоторыми примерами, которые нашел в Интернете, но если это не сработает, относитесь к нему как к псевдокоду. У меня также есть полный рабочий пример Python ниже, который при желании можно использовать в качестве основы.
Опять же, вы можете найти это излишество для своего конкретного примера, но он создает гораздо более чистый код, если в конечном итоге вы получите гораздо более сложный набор случаев, которые необходимо выполнить.
источник
Простой подход - найти ответ, который вы считаете приемлемым.
Да = (boolean1 && boolean2 && boolean3 && boolean4) + + ...
Теперь, если возможно, упростите уравнение с помощью булевой алгебры.
как и в этом случае, допустимые1 и 2 объединяются в
(boolean1 && boolean2 && boolean3)
.Отсюда окончательный ответ:
источник
использовать битовое поле :
PS :
Это большая жалость для CPP. Но UB меня не беспокоит, проверьте его на http://coliru.stacked-crooked.com/a/2b556abfc28574a1 .
источник
unsigned char*
, хотя я думаю, что простое использование чего-то вроде((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1
, вероятно, было бы более эффективным.