Мой вопрос:
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этоtrueelse-блок в логике вашего вопроса должен condition Aбыть trueи condition Bдолжен быть false- Единственный способ, которым мы можем достичь else-блока в этой логике, был бы, если бы condition Aбыл trueи condition Bбылfalseelse-блок в логике вашего вопроса должен 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последний раз