Моє запитання:
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 (!A || B) {
action C
} else {
...
}
Щоб дізнатися більше про подібні вирази, я пропоную гуглінг "булева алгебра", "логіка предиката" та "обчислення предиката". Це глибокі математичні теми. Вам не потрібно все це вивчати, лише основи.
Ви також повинні дізнатися про "оцінку короткого замикання". Через це важливий порядок виразів, щоб точно дублювати вашу оригінальну логіку. Хоча B || !A
логічно еквівалентно, використовуючи це, оскільки умова виконує "дію C", коли B
це істинно, незалежно від значення A
.
...
є . Якщо це взагалі нічого (тобто "зробіть C, якщо ці умови виконані; інакше нічого не робіть"), то це, безумовно, найкраще рішення, оскільки 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
для чого стоїть.
Мовою зі збігом шаблонів ви можете висловити рішення таким чином, що більш прямо відображає таблицю істинності у відповіді QuestionC.
match (a,b) with
| (true,false) -> ...
| _ -> action c
Якщо ви не знайомі з синтаксисом, кожен візерунок представлений символом | Далі слід відповідати значенням (a, b), а підкреслення використовується як підмітка для позначення "будь-яких інших значень". Оскільки єдиний випадок, коли ми хочемо зробити щось, крім дії c, - це коли істина, а b - хибна, ми явно констатуємо ці значення як перший зразок (true, false), а потім робимо все, що слід зробити в цьому випадку. У всіх інших випадках ми переходимо до схеми "підстановки" і робимо дії c.
Постановка проблеми:
Якщо умова A збігається, для виконання дії C потрібно відповідати умові B
описує значення : 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
-statement то ми знаємо , що 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)
. а | ! 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
пункти з однією річчю в кінці майже завжди повинні бути перевернуті, якщо це можливо. Ось покроковий приклад:
Витяг С:
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
останній раз