Якщо умова A збігається, для виконання дії C потрібно відповідати умові B


148

Моє запитання:

if (/* condition A */)
{
    if(/* condition B */)
      {
         /* do action C */
      }
    else
      /* ... */
}
else
{
   /* do action C */
}

Чи можна просто написати код дії C один раз замість двічі?

Як її спростити?


56
Покласти код функції "C" у функцію?
CinCout

26
Це сумно, що насправді це не стосується C ++ питання яке надійшло до HNQ: /
YSC

2
Дякую всім за допомогу! На початку я просто хочу переконатися, що все гаразд, тому я використав вкладений, якщо. Це тому, що це найпростіший спосіб, який я здогадався. Я постараюся докласти більше зусиль, задавши питання наступного разу. Бажаю всім приємного дня :)
starf15h

13
Це дуже гарна стратегія: напишіть код, який працює спочатку, а потім переживайте, щоб зробити його елегантним та ефективним пізніше.
Код-учень

3
@Tim я написав це як відповідь. Зі сторони, сумно бачити там менше голосів.
CinCout

Відповіді:


400

Ваш перший крок у подібних проблемах - це завжди створити логічну таблицю.

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
}

Зауважте, що ця логіка, хоча й коротша, майбутнім програмістам може бути важкою у дотриманні.


35
Мені дуже подобається, що ти показав таблицю правди, щоб допомогти ОП зрозуміти, як саме це розвивати. Чи можете ви зробити цей крок далі і пояснити, як ви отримуєте бульне вираження з таблиці істинності? Для когось нового в програмуванні та булевій логіці це, мабуть, зовсім не зрозуміло.
Код-учень

14
Якщо оцінка Bмає побічні ефекти, логічна таблиця повинна враховувати це.
Якк - Адам Невраумон

79
@Yakk Моя відповідь не стосується побічних ефектів з двох причин. По-перше, рішення має (випадково) правильну поведінку побічних ефектів. По-друге, і що ще важливіше, A і B, які мають побічні ефекти, були б поганим кодом, і обговорення цього випадку було б відволіканням на питання, принципово про булеву логіку.
QuestionC

52
Можливо, варто відзначити, якщо A && !Bвипадок заборонений: !(A && !B)це рівнозначно тому, !A || Bщо ви можете зробити if (!A || B) { /* do action C */ }і уникнути порожнього блоку.
KRyan

54
Якщо if (A && !B)майбутнім програмістам справді важко підтримувати, то допомогти їм насправді немає.
Рей

65

У вас є два варіанти:

  1. Напишіть функцію, яка виконує "дію С".

  2. Перестановіть свою логіку так, щоб у вас не було стільки вкладених заяв. Запитайте себе, які умови викликають "дію С". Мені здається, що це відбувається, коли або "умова В" є істинною, або "умова А" хибною. Ми можемо записати це як "НЕ АБО Б". Перекладаючи це в код C, ми отримуємо

    if (!A || B) {
        action C
    } else {
        ...
    }
    

Щоб дізнатися більше про подібні вирази, я пропоную гуглінг "булева алгебра", "логіка предиката" та "обчислення предиката". Це глибокі математичні теми. Вам не потрібно все це вивчати, лише основи.

Ви також повинні дізнатися про "оцінку короткого замикання". Через це важливий порядок виразів, щоб точно дублювати вашу оригінальну логіку. Хоча B || !Aлогічно еквівалентно, використовуючи це, оскільки умова виконує "дію C", коли Bце істинно, незалежно від значення A.


15
@Yakk Дивіться закони deMorgan.
Код-учень

@ Код-учень, будь ласка, вибачте моє погане логічне мислення. Я хотів би запитати, чи є різниця між (! A || B) і (A&&! B). Здається, обидва в порядку з моєю проблемою. Я маю на увазі ваш підхід і питання QuestionC.
starf15h

6
@ Starf15h Є ще одна важлива різниця: де виконується "дія С". Ця різниця робить наші два рішення рівнозначними. Я пропоную вам google "Закони deMorgan", які повинні допомогти вам зрозуміти, що тут відбувається.
Код-учень

5
Два рішення абсолютно рівноцінні, але може бути практична різниця залежно від того, що саме ...є . Якщо це взагалі нічого (тобто "зробіть C, якщо ці умови виконані; інакше нічого не робіть"), то це, безумовно, найкраще рішення, оскільки elseвисловлювання тоді просто може бути залишене зовсім.
Жакус Бахс Жак

1
Також, залежно від імен A і B, ця схема може бути більш читаною або менш читаною для людини, ніж домовленість QuestionC.
Майкл - Де Клей Ширкий

15

Ви можете спростити твердження так:

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
}

7
Або простішеif (!A || B)
Tas

2
Логічно, ((A&& B) ||! A) еквівалентно (B ||! A)
Код-учень

@ Code-Apprentice B || !Aпризведе до trueлише, якщо Bє true, не фактично перевіряючи його Aчерез коротке замикання
CinCout

1
@CinCout Добре. Хоча моє твердження все ще вірно з теоретичної булевої логічної точки зору, я не враховував практичності булевих операторів короткого замикання. На щастя, моя власна відповідь має правильний порядок.
Код-учень

1
Тож з точки зору логіки порядок значення не має. Однак, з точки зору технічного обслуговування та читабельності, може залежати велика різниця залежно від того, що саме Aі Bдля чого стоїть.
Код-учень

14

Мовою зі збігом шаблонів ви можете висловити рішення таким чином, що більш прямо відображає таблицю істинності у відповіді QuestionC.

match (a,b) with
| (true,false) -> ...
| _ -> action c

Якщо ви не знайомі з синтаксисом, кожен візерунок представлений символом | Далі слід відповідати значенням (a, b), а підкреслення використовується як підмітка для позначення "будь-яких інших значень". Оскільки єдиний випадок, коли ми хочемо зробити щось, крім дії c, - це коли істина, а b - хибна, ми явно констатуємо ці значення як перший зразок (true, false), а потім робимо все, що слід зробити в цьому випадку. У всіх інших випадках ми переходимо до схеми "підстановки" і робимо дії c.


10

Постановка проблеми:

Якщо умова 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 ++?
einpoklum

@einpoklum Я не потрапив у деякі з цих деталей, оскільки це питання насправді не вказувало мови (але наводив приклад із синтаксисом подібного С), тому я дав відповідь із синтаксисом, подібним С. Особисто я би використовував макрос, щоб умова B не оцінювалася без потреби.
jamesdlin

6

Цього, це мене і спричинило, але, як зазначає Code-Apprentice, нам гарантовано потрібно do action Cабо запустити вкладений elseблок, таким чином код можна спростити до:

if (not condition A or condition B) {
    do action C
} else {
    ...
}

Ось як ми потрапляємо у 3 випадки:

  1. Вкладений do action Cв логіці вашого питання вимагав condition Aі condition Bбути true- в цій логіці, якщо ми досягаємо 2 - й члена в if-statement то ми знаємо , що condition Aцеtrue таким чином , все , що нам потрібно оцінити, що condition Bєtrue
  2. Вкладений else-блок в логіці вашого питання, який потрібно condition Aбути trueі condition Bбути false- Єдиним способом, яким ми можемо дійти до else-блока в цій логіці, було б, якби condition Aбули trueіcondition B булиfalse
  3. Зовнішній else-блок у логіці вашого запитання повинен condition Aбути false- У цій логіці, якщо condition Aпомилковою, ми такожdo action C

Реквізит Code-Apprentice для випрямлення мене тут. Я б запропонував прийняти його відповідь , оскільки він подав її правильно, не редагуючи: /


2
Зауважте, що "стан А" не потрібно оцінювати заново. У C ++ маємо закон виключеної середини. Якщо "умова A" невірна, тоді "умова A" обов'язково відповідає дійсності.
Код-учень

1
Через коротке замикання, Bоцінюватимуться лише у випадку !Aпомилки. Тому обидва повинні провалитися, щоб виконати elseзаяви.
Код-учень

Навіть без оцінки короткого замикання !A || Bє помилковим саме тоді, коли і те, !Aі інше B. Тому Aбуде істинним, коли elseстрачує. Не потрібно переоцінювати A.
Код-учень

@ Code-Apprentice Добре смердію, чудове спостереження, я виправив свою відповідь, але запропонував прийняти вашу. Я просто намагаюся пояснити, що ви вже висунули.
Джонатан Мі

Я б хотів, щоб я міг дати вам ще один голос за пояснення кожного випадку.
Код-учень

6

У логічній концепції ви можете вирішити цю проблему наступним чином:

f = ab +! a
f =?

Як доведена проблема, це призводить до f = !a + b. Є кілька способів довести проблему, наприклад, таблиця правди, карта Карно тощо.

Тож на мовах, що базуються на C, ви можете використовувати наступне:

if(!a || b)
{
   // Do action C
}

PS: Карта Карно також використовується для більш складних серій умов. Це метод спрощення виразів булевої алгебри.


6

Незважаючи на те, що вже є хороші відповіді, я подумав, що цей підхід може бути ще більш інтуїтивним для того, хто є новим у булевій алгебрі, ніж оцінити таблицю істинності.

Перше, що ви хочете зробити, це подивитися, за яких умов ви хочете виконати 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.
}

Таким чином ви також можете мінімізувати терміни за допомогою більшої кількості операндів, що швидко стає некрасивим з таблицями істинності. Ще один хороший підхід - карти Карно. Але я зараз не буду заглиблюватися в це.


4

Щоб код виглядав як текст, використовуйте булові прапори. Якщо логіка особливо незрозуміла, додайте коментарі.

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
  {
  ...
  }


2

Я б витяг 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останній раз


2

Використання прапорів також може вирішити цю проблему

int flag = 1; 
if ( condition A ) {
    flag = 2;
    if( condition B ) {
        flag = 3;
    }
}
if(flag != 2) { 
    do action C 
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.