Мой вопрос:
if (/* condition A */)
{
if(/* condition B */)
{
/* do action C */
}
else
/* ... */
}
else
{
/* do action C */
}
Можно ли просто написать код действия C один раз вместо двух?
Как это упростить?
Мой вопрос:
if (/* condition A */)
{
if(/* condition B */)
{
/* do action C */
}
else
/* ... */
}
else
{
/* do action C */
}
Можно ли просто написать код действия C один раз вместо двух?
Как это упростить?
Ответы:
Ваш первый шаг в решении подобных проблем - создание логической таблицы.
A | B | Result
-------------------
T | T | do action C
T | F | ...
F | T | do action C
F | F | do action C
Как только вы сделали таблицу, решение станет ясным.
if (A && !B) {
...
}
else {
do action C
}
Обратите внимание, что эта логика, хотя и короче, может быть трудной для будущих программистов.
B
имеет побочные эффекты, логическая таблица должна это учитывать.
A && !B
дело не в опере: !(A && !B)
это эквивалентно тому, !A || B
что вы можете сделать if (!A || B) { /* do action C */ }
и избежать пустого блока.
if (A && !B)
будущим программистам действительно трудно поддерживать, то им действительно нет никакой помощи.
У вас есть два варианта:
Напишите функцию, которая выполняет «действие C».
Переставьте свою логику, чтобы у вас не было так много вложенных операторов if. Спросите себя, какие условия вызывают «действие С». Мне кажется, что это происходит, когда «условие В» истинно или «условие А» ложно. Мы можем написать это как «НЕ А ИЛИ Б». Переводя это в C-код, мы получаем
if (!A || B) {
action C
} else {
...
}
Чтобы узнать больше о такого рода выражениях, я предлагаю поискать «булеву алгебру», «логику предикатов» и «исчисление предикатов». Это глубокие математические темы. Вам не нужно изучать все это, только основы.
Вы также должны узнать о «оценке короткого замыкания». Поэтому порядок выражений важен для точного дублирования вашей исходной логики. Хотя B || !A
логически эквивалентно, использование этого в качестве условия будет выполнять «действие C», когда B
оно истинно, независимо от значения A
.
...
есть практическое различие . Если это вообще ничего (т. Е. «Делай С, если эти условия выполняются; в противном случае ничего не делай»), тогда это явно лучшее решение, так как тогда это else
утверждение можно просто пропустить.
Вы можете упростить утверждение следующим образом:
if ((A && B) || (!A)) // or simplified to (!A || B) as suggested in comments
{
do C
}
В противном случае поместите код для «C» в отдельную функцию и вызовите его:
DoActionC()
{
....
// code for Action C
}
if (condition A)
{
if(condition B)
{
DoActionC(); // call the function
}
else
...
}
else
{
DoActionC(); // call the function
}
if (!A || B)
B || !A
будет работать true
только в том случае , если B
есть true
, без фактической проверки на наличие A
короткого замыкания
A
и что B
означает.
На языке с сопоставлением с образцом вы можете выразить решение так, чтобы оно более непосредственно отражало таблицу истинности в ответе на вопрос C.
match (a,b) with
| (true,false) -> ...
| _ -> action c
Если вы не знакомы с синтаксисом, каждый шаблон представлен знаком | за ними следуют значения, совпадающие с (a, b), и подчеркивание используется в качестве подстановочного знака для обозначения «любых других значений». Поскольку единственный случай, когда мы хотим сделать что-то кроме действия c, это когда a истинно, а b ложно, мы явно указываем эти значения как первый шаблон (истина, ложь) и затем делаем все, что должно быть сделано в этом случае. Во всех остальных случаях мы переходим к шаблону «подстановочный знак» и выполняем действие c.
Постановка проблемы:
Если условие A соответствует, условие B должно соответствовать, чтобы выполнить действие C
описывает импликацию : A подразумевает B , логическое утверждение, эквивалентное !A || B
(как упомянуто в других ответах):
bool implies(bool p, bool q) { return !p || q; }
if (implies(/* condition A */,
/* condition B */))
{
/* do action C */
}
inline
для C, constexpr
а также для C ++?
Тьфу, это тоже сбило меня с толку , но, как указывает Code-Apprentice, нам гарантированно понадобится do action C
или запустит вложенный else
блок, поэтому код можно упростить до:
if (not condition A or condition B) {
do action C
} else {
...
}
Вот как мы попали в 3 случая:
do action C
в логику вашего вопроса требуется condition A
и condition B
должна быть ... true
В этой логике, если мы достигнем 2- го слагаемого в if
утверждении, тогда мы знаем, что condition A
, true
таким образом, все, что нам нужно оценить, condition B
этоtrue
else
-блок в логике вашего вопроса должен condition A
быть true
и condition B
должен быть false
- Единственный способ, которым мы можем достичь else
-блока в этой логике, был бы, если бы condition A
был true
и condition B
былfalse
else
-блок в логике вашего вопроса должен condition A
быть false
- В этой логике, если condition A
ложь, мы такжеdo action C
Реквизиты Code-Apprentice за то, что выправили меня здесь. Я бы предложил принять его ответ , так как он представил его правильно, без редактирования: /
B
будет оцениваться, только если !A
ложно. Таким образом, оба должны потерпеть неудачу для выполнения else
операторов.
!A || B
ложна именно тогда, когда оба !A
и B
ложны. Следовательно, A
будет верно, когда else
исполняется. Не нужно переоценивать A
.
В концепции логики вы можете решить эту проблему следующим образом:
f = ab +! a
f =?
Как доказанная проблема, это приводит к f = !a + b
. Есть несколько способов доказать эту проблему, такие как таблица истинности, карта Карно и так далее.
Так что в языках на основе C вы можете использовать следующее:
if(!a || b)
{
// Do action C
}
PS: Карта Карно также используется для более сложной серии условий. Это метод упрощения выражений булевой алгебры.
Хотя уже есть хорошие ответы, я подумал, что этот подход может быть еще более интуитивным для того, кто плохо знаком с булевой алгеброй, чем для оценки таблицы истинности.
Первое, что вы хотите сделать, это посмотреть, при каких условиях вы хотите выполнить C. Это тот случай, когда (a & b)
. Также когда !a
. Так и есть (a & b) | !a
.
Если вы хотите свести к минимуму, вы можете продолжать. Как и в «обычной» арифметике, вы можете умножить.
(a & b) | !a = (a | !a) & (b | !a)
, а | ! А всегда верно, так что вы можете просто пересечь его, который оставляет вас с свернутым результатом: b | !a
. В случае, если порядок имеет значение, потому что вы хотите проверить b, только если! A истинно (например, когда! A является проверкой нулевого указателя, а b является операцией над указателем, как @LordFarquaad указал в его комментарии), вы можете хочу поменять два.
Другой случай (/ * ... * /) всегда будет выполняться, когда c не выполняется, поэтому мы можем просто поместить его в другой случай.
Также стоит упомянуть, что, вероятно, имеет смысл использовать способ c в методе.
Что оставляет нас со следующим кодом:
if (!A || B)
{
doActionC() // execute method which does action C
}
else
{
/* ... */ // what ever happens here, you might want to put it into a method, too.
}
Таким образом, вы также можете минимизировать термины с помощью большего числа операндов, что быстро уродливо с таблицами истинности. Еще один хороший подход - карты Карно. Но я не буду углубляться в это сейчас.
Чтобы код выглядел больше как текст, используйте логические флаги. Если логика особенно неясна, добавьте комментарии.
bool do_action_C;
// Determine whether we need to do action C or just do the "..." action
// If condition A is matched, condition B needs to be matched in order to do action C
if (/* condition A */)
{
if(/* condition B */)
do_action_C = true; // have to do action C because blah
else
do_action_C = false; // no need to do action C because blarg
}
else
{
do_action_C = true; // A is false, so obviously have to do action C
}
if (do_action_C)
{
DoActionC(); // call the function
}
else
{
...
}
Я извлек бы C в метод, а затем выйду из функции как можно скорее во всех случаях. else
пункты с одной вещью в конце почти всегда должны быть инвертированы, если это возможно. Вот пошаговый пример:
Извлечь C:
if (A) {
if (B)
C();
else
D();
} else
C();
Обрати сначала, if
чтобы избавиться от первого else
:
if (!A) {
C();
return;
}
if (B)
C();
else
D();
Избавьтесь от второго else
:
if (!A) {
C();
return;
}
if (B) {
C();
return;
}
D();
И тогда вы можете заметить, что два случая имеют одинаковое тело и могут быть объединены:
if (!A || B) {
C();
return;
}
D();
Необязательные вещи для улучшения будут:
зависит от контекста, но если !A || B
сбивает с толку, извлеките его в одну или несколько переменных, чтобы объяснить намерение
какой бы из C()
или D()
не был исключительный случай должен идти последним, так что если D()
это исключение, то инвертировать if
последний раз