Мой вопрос:
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
}
Обратите внимание, что эта логика, хотя и короче, может быть трудной для будущих программистов.
У вас есть два варианта:
Напишите функцию, которая выполняет "действие C" .
Измените свою логику так, чтобы у вас не было так много вложенных операторов if. Спросите себя, какие условия вызывают "действие C" . Мне кажется, что это происходит, когда либо "условие B" истинно, либо "условие A" является ложным. Мы можем написать это как "НЕ А ИЛИ В". Переведя это на C-код, получим
if (!A || B) {
action C
} else {
...
}
Чтобы узнать больше об этих выражениях, я предлагаю googling "логическая алгебра", "предикатная логика" и "исчисление предикатов". Это глубокие математические темы. Вам не нужно изучать все, просто основы.
Вы также должны узнать о "оценке короткого замыкания". Из-за этого порядок выражений важен для точного дублирования исходной логики. Хотя B || !A
логически эквивалентен, используя это как условие, будет выполняться "действие C" , когда B
истинно независимо от значения A
.
Вы можете упростить утверждение следующим образом:
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
}
В языке с сопоставлением с образцом вы можете выразить это решение таким образом, чтобы более прямо отражать таблицу истинности в ответе QuestionC.
match (a,b) with
| (true,false) -> ...
| _ -> action c
Если вы не знакомы с синтаксисом, каждый шаблон представлен символом | а затем значения, соответствующие (a, b), а подчеркивание используется в качестве подстановочного знака для обозначения "любых других значений". Поскольку единственный случай, когда мы хотим сделать что-то другое, кроме действия c, - это когда true и b является ложным, мы явно указываем эти значения как первый шаблон (true, false), а затем делаем все, что нужно сделать в этом случае. Во всех остальных случаях мы переходим к шаблону "подстановочные знаки" и делаем действие c.
Оператор проблемы:
Если выполняется условие A, условие B необходимо согласовать, чтобы выполнить действие C
описывает implication: A подразумевает B, логическое предложение, эквивалентное !A || B
(как упоминалось в других ответах):
bool implies(bool p, bool q) { return !p || q; }
if (implies(/* condition A */,
/* condition B */))
{
/* do action C */
}
Ух, это тоже сработало, но как указанному Code-Apprentice, нам гарантированно нужно do action C
или запустите nested- else
, поэтому код можно упростить до:
if (not condition A or condition B) {
do action C
} else {
...
}
Вот как мы сталкиваемся с тремя случаями:
do action C
в вашей логике вопроса требуется condition A
и condition B
быть true
. В этой логике, если мы достигнем члена 2 nd в if
-statement, то мы знаем, что condition A
true
, поэтому все, что нам нужно оценить, состоит в том, что condition B
есть true
else
-блока в вашей логике вопроса требуется condition A
быть true
и condition B
как false
- единственный способ, которым мы можем достичь else
-блока в этой логике было бы, если condition A
были true
и condition B
были false
else
-block в вашей логике вопроса требуется condition A
быть false
- В этой логике, если condition A
является ложным, мы также do action C
Репутация для Ученика Кодекса для выпрямления меня здесь. Я предлагаю принять его ответ, так как он правильно его представил без редактирования:/
Несмотря на то, что уже есть хорошие ответы, я подумал, что этот подход может быть еще более интуитивным для того, кто является новым для булевой алгебры, а затем для оценки таблицы истинности.
Первое, что вы хотите сделать, это посмотреть, в каких условиях вы хотите выполнить C. Это тот случай, когда (a & b)
. Также при !a
.
Итак, у вас есть (a & b) | !a
.
Если вы хотите свести к минимуму, вы можете продолжать. Как и в "нормальных" арифметиках, вы можете размножаться.
(a & b) | !a = (a | !a) & (b | !a)
.
a |! a всегда верно, поэтому вы можете просто перечеркнуть его, что оставляет вас с минимальным результатом: b | !a
.
В случае, если порядок имеет значение, потому что вы хотите проверить b только в том случае, если! A истинно (например, когда! A - проверка с нулевым указателем, а b - операция над указателем, подобным @LordFarquaad, указанному в его комментарии), вы можете хотите переключить два.
Другой случай (/*... */) всегда будет выполняться, когда c не выполняется, поэтому мы можем просто положить его в случай else.
Также стоит упомянуть, что, вероятно, имеет смысл в любом случае поместить действие 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.
}
Таким образом вы также можете свести к минимуму термины с большим количеством операндов, которые быстро становятся уродливыми с таблицами истинности. Другим хорошим подходом являются карты Карно. Но я не буду углубляться в это сейчас.
В логической концепции вы можете решить эту проблему следующим образом:
f = a.b +! a
f =?
Как доказанная проблема, это приводит к f = !a + b
.
Есть несколько способов доказать эту проблему, например таблицу истинности, Карта Карно и т.д.
Итак, на языках с языком C вы можете использовать следующее:
if(!a || b)
{
// Do action C
}
P.S.: Карта Карно также используется для более сложных серий условий. Это метод упрощения выражений булевой алгебры.
Чтобы код выглядел больше как текст, используйте логические флаги. Если логика особенно неясна, добавьте комментарии.
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
{
...
}
if((A && B ) || !A)
{
//do C
}
else if(!B)
{
//...
}
Я бы извлек 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
в последний раз
Использование флагов также может решить эту проблему
int flag = 1;
if ( condition A ) {
flag = 2;
if( condition B ) {
flag = 3;
}
}
if(flag != 2) {
do action C
}