Моє запитання:
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є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). а | ! 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останній раз