Jika kondisi A cocok, kondisi B perlu dicocokkan untuk melakukan tindakan C

148

Pertanyaanku adalah:

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

Apakah mungkin untuk hanya menulis kode tindakan C satu kali, bukan dua kali?

Bagaimana cara menyederhanakannya?

starf15h
sumber
56
Masukkan kode untuk "action C" dalam suatu fungsi?
CinCout
26
Ini menyedihkan ini tidak benar-benar terkait dengan pertanyaan C ++ sampai ke HNQ: /
YSC
2
Terima kasih semuanya telah membantu saya! Pada awalnya, saya hanya ingin memastikan semuanya baik-baik saja jadi saya menggunakan bersarang jika. Itu karena ini adalah cara paling sederhana yang saya duga. Saya akan mencoba melakukan lebih banyak upaya setelah mengajukan pertanyaan lain kali.
Semoga
13
Itu adalah strategi yang sangat baik: menulis kode yang berfungsi lebih dulu, lalu khawatir membuatnya elegan dan efisien nantinya.
Code-Apprentice
3
@Tim saya memang mempostingnya sebagai jawaban. Di samping catatan, sedih melihat lebih sedikit suara di sana.
CinCout

Jawaban:

400

Langkah pertama Anda dalam masalah seperti ini adalah selalu membuat tabel logika.

A | B | Result
-------------------
T | T | do action C
T | F | ...
F | T | do action C
F | F | do action C

Setelah Anda membuat tabel, solusinya jelas.

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

Perhatikan bahwa logika ini, walaupun lebih pendek, mungkin sulit untuk diprogram oleh programmer di masa depan.

QuestionC
sumber
35
Saya sangat suka Anda menunjukkan tabel kebenaran untuk membantu OP memahami bagaimana mengembangkan ini sendiri. Bisakah Anda mengambil langkah lebih jauh dan menjelaskan bagaimana Anda mendapatkan ekspresi boolean dari tabel kebenaran? Untuk seseorang yang baru mengenal pemrograman dan logika boolean, ini mungkin sama sekali tidak jelas.
Code-Apprentice
14
Jika mengevaluasi Bmemiliki efek samping, tabel logika harus menjelaskannya.
Yakk - Adam Nevraumont
79
@ Yakk Jawaban saya tidak membahas efek samping karena dua alasan. Pertama, solusinya memang (secara kebetulan) memiliki perilaku efek samping yang benar. Kedua, dan yang lebih penting, A dan B memiliki efek samping akan menjadi kode yang buruk dan diskusi tentang kasus pinggiran akan menjadi gangguan untuk pertanyaan secara mendasar tentang logika boolean.
QuestionC
52
Mungkin perlu dicatat, dalam kasus A && !Bini adalah no-op: !(A && !B)setara dengan !A || Byang berarti Anda dapat melakukan if (!A || B) { /* do action C */ }dan menghindari blok kosong.
KRyan
54
Jika if (A && !B)benar-benar sulit untuk dipelihara oleh programmer di masa depan, maka tidak ada yang bisa membantu mereka.
Ray
65

Anda memiliki dua opsi:

  1. Tulis fungsi yang melakukan "tindakan C".

  2. Atur ulang logika Anda sehingga Anda tidak memiliki begitu banyak pernyataan bersarang jika. Tanyakan pada diri Anda kondisi apa yang menyebabkan "tindakan C" terjadi. Sepertinya saya terjadi ketika "kondisi B" benar atau "kondisi A" salah. Kita dapat menulis ini sebagai "BUKAN ATAU B". Menerjemahkan ini ke dalam kode C, kita dapatkan

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

Untuk mempelajari lebih lanjut tentang ekspresi semacam ini, saya sarankan googling "aljabar boolean", "logika predikat", dan "predikat kalkulus". Ini adalah topik matematika yang mendalam. Anda tidak perlu mempelajari semuanya, hanya dasar-dasarnya.

Anda juga harus belajar tentang "evaluasi hubung singkat". Karena itu, urutan ekspresi penting untuk menduplikasi logika asli Anda secara tepat. Meskipun B || !Asecara logika setara, menggunakan ini sebagai kondisi akan mengeksekusi "aksi C" ketika Bbenar terlepas dari nilai A.

Magang-Kode
sumber
15
@Yakk See deMorgan's Laws.
Code-Apprentice
@ Code-Apprentice Tolong maafkan pemikiran logis saya yang buruk. Saya ingin bertanya apakah ada perbedaan antara (! A || B) dan (A &&! B). Sepertinya keduanya baik-baik saja untuk masalah saya. Maksud saya pendekatan Anda dan QuestionC.
starf15h
6
@ Starf15h Ada satu perbedaan penting lainnya: di mana "aksi C" dijalankan. Perbedaan ini membuat dua solusi kami persis sama. Saya sarankan Anda mencari "deMorgan's Laws" di google yang akan membantu Anda memahami apa yang sedang terjadi di sini.
Code-Apprentice
5
Kedua solusi yang persis sama, tapi mungkin ada perbedaan praktis tergantung pada apa yang sebenarnya ...adalah . Jika tidak ada sama sekali (yaitu, "lakukan C jika kondisi ini terpenuhi; jika tidak lakukan apa-apa"), maka ini jelas merupakan solusi yang unggul, karena elsepernyataan itu bisa ditinggalkan begitu saja.
Janus Bahs Jacquet
1
Juga, tergantung pada nama A dan B, pengaturan ini mungkin lebih mudah dibaca atau kurang dapat dibaca oleh manusia daripada pengaturan QuestionC.
Michael - Di mana Clay Shirky
15

Anda dapat menyederhanakan pernyataan seperti ini:

if ((A && B) || (!A)) // or simplified to (!A || B) as suggested in comments
{
    do C
}

Kalau tidak, letakkan kode untuk 'C' di fungsi terpisah dan panggil:

DoActionC()
{
    ....
    // code for Action C
}
if (condition A)
{
    if(condition B)
      {
         DoActionC(); // call the function
      }
    else
      ...
}
else
{
   DoActionC(); // call the function
}
CinCout
sumber
7
Atau lebih sederhanaif (!A || B)
Tas
2
Logikanya, ((A&& B) ||! A) setara dengan (B ||! A)
Code-Apprentice
@ Code-Apprentice hanya B || !Aakan menghasilkan truejika Bada true, tanpa benar-benar memeriksa Akarena korsleting
CinCout
1
@CinCout Poin bagus. Sementara pernyataan saya masih benar dari perspektif logika boolean teoretis, saya tidak memperhitungkan kepraktisan operator boolean hubung singkat. Untungnya, jawaban saya sendiri memiliki urutan yang benar.
Code-Apprentice
1
Jadi dari perspektif logika, urutannya tidak masalah. Namun, dari sudut pandang pemeliharaan dan keterbacaan, mungkin ada perbedaan besar tergantung pada apa sebenarnya Adan Bberdiri untuk.
Code-Apprentice
14

Dalam bahasa dengan pencocokan pola, Anda dapat mengekspresikan solusi dengan cara yang lebih langsung mencerminkan tabel kebenaran dalam jawaban QuestionC.

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

Jika Anda tidak terbiasa dengan sintaks, setiap pola diwakili oleh | diikuti oleh nilai yang cocok dengan (a, b), dan garis bawah digunakan sebagai wildcard yang berarti "nilai lain". Karena satu-satunya kasus di mana kita ingin melakukan sesuatu selain tindakan c adalah ketika a benar dan b salah, kita secara eksplisit menyatakan nilai-nilai itu sebagai pola pertama (benar, salah) dan kemudian melakukan apa pun yang harus dilakukan dalam kasus itu. Dalam semua kasus lain, kita masuk ke pola "wildcard" dan melakukan tindakan c.

Aaron M. Eshbach
sumber
10

Pernyataan masalah:

Jika kondisi A cocok, kondisi B perlu dicocokkan untuk melakukan tindakan C

menggambarkan implikasi : A menyiratkan B , proposisi logis yang setara dengan !A || B(sebagaimana disebutkan dalam jawaban lain):

bool implies(bool p, bool q) { return !p || q; }

if (implies(/* condition A */,
            /* condition B */))
{
    /* do action C */
}
jamesdlin
sumber
Mungkin tandai inlineuntuk C dan constexprjuga untuk C ++?
einpoklum
@einpoklum Saya tidak masuk ke beberapa detail itu karena pertanyaan ini tidak benar-benar menentukan bahasa (tapi memberikan contoh dengan sintaks mirip C), jadi saya memberikan jawaban dengan sintaks mirip C. Secara pribadi saya akan menggunakan makro sehingga kondisi B tidak dievaluasi secara tidak perlu.
jamesdlin
6

Ugh, ini juga membuat saya tersandung, tetapi seperti yang ditunjukkan oleh Code-Apprentice, kami dijamin perlu do action Catau menjalankan elseblok bersarang , sehingga kode dapat disederhanakan untuk:

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

Inilah cara kami menekan 3 kasus:

  1. Bersarang do action C dalam logika pertanyaan Anda diperlukan condition Adan condition Bmenjadi true- Dalam logika ini, jika kita mencapai 2 nd istilah dalam ifpernyataan-maka kita tahu bahwa condition Aadalah truesehingga semua kita perlu mengevaluasi adalah bahwa condition Badalahtrue
  2. Bersarang elseBlok- dalam logika pertanyaan Anda harus condition Aada truedan condition Bmenjadifalse - Satu-satunya cara bahwa kita dapat mencapai else-block dalam logika ini akan jika condition Aberada truedan condition Byangfalse
  3. Bagian luar elseBlok- dalam logika pertanyaan Anda condition Aharus false- Dalam logika ini, jika condition Aitu salah, kami jugado action C

Alat Peraga untuk Magang-Kode untuk meluruskan saya di sini. Saya sarankan menerima jawabannya , karena ia menyajikannya dengan benar tanpa mengedit: /

Jonathan Mee
sumber
2
Perhatikan bahwa "kondisi A" tidak perlu dievaluasi lagi. Dalam C ++, kami memiliki hukum dari perantara yang dikecualikan. Jika "bukan kondisi A" salah, maka "kondisi A" harus benar.
Code-Apprentice
1
Karena evaluasi hubung singkat, Bakan dievaluasi hanya jika !Asalah. Jadi keduanya harus gagal untuk menjalankan elsepernyataan.
Code-Apprentice
Bahkan tanpa hubung singkat evaluasi !A || Badalah salah persis ketika keduanya !Adan Bitu salah. Karena itu, Aakan benar ketika elsedieksekusi. Tidak perlu mengevaluasi kembali A.
Code-Apprentice
@ Code-Apprentice Bau, pengamatan yang bagus, saya sudah mengoreksi jawaban saya, tetapi menyarankan Anda diterima. Saya hanya mencoba menjelaskan apa yang sudah Anda kemukakan.
Jonathan Mee
Saya berharap saya bisa memberikan Anda suara untuk menjelaskan setiap kasus.
Code-Apprentice
6

Dalam konsep logika, Anda bisa menyelesaikan masalah ini sebagai berikut:

f = ab +! a
f =?

Sebagai masalah yang terbukti, ini menghasilkan f = !a + b. Ada beberapa cara untuk membuktikan masalah seperti tabel kebenaran, Karnaugh Map dan sebagainya.

Jadi, dalam bahasa berbasis C Anda dapat menggunakan sebagai berikut:

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

PS: Karnaugh Map juga digunakan untuk serangkaian kondisi yang lebih rumit. Ini adalah metode penyederhanaan ekspresi aljabar Boolean.

Siyavash Hamdi
sumber
6

Meskipun sudah ada jawaban yang bagus, saya berpikir bahwa pendekatan ini mungkin lebih intuitif bagi seseorang yang baru mengenal aljabar Boolean kemudian mengevaluasi tabel kebenaran.

Hal pertama yang ingin Anda lakukan adalah melihat, di bawah kondisi apa Anda ingin mengeksekusi C. Ini adalah kasus ketika (a & b). Juga kapan !a. Jadi kamu punya (a & b) | !a.

Jika Anda ingin meminimalkan, Anda dapat melanjutkan. Sama seperti dalam aritmatika "normal", Anda dapat mengalikan.

(a & b) | !a = (a | !a) & (b | !a). a | ! a selalu benar, jadi Anda bisa mencoretnya, yang membuat Anda dengan hasil minimal:b | !a . Dalam hal urutan membuat perbedaan, karena Anda ingin memeriksa b hanya jika! A benar (misalnya ketika! A adalah cek nullpointer dan b adalah operasi pada pointer seperti @LordFarquaad ditunjukkan dalam komentarnya), Anda mungkin ingin mengganti keduanya.

Kasus lain (/ * ... * /) akan selalu dieksekusi ketika c tidak dieksekusi, jadi kita bisa memasukkannya ke dalam kasus lain.

Yang juga layak disebutkan adalah bahwa mungkin masuk akal untuk memasukkan tindakan c ke dalam metode.

Yang membuat kita dengan kode berikut:

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

Dengan cara ini Anda juga dapat meminimalkan istilah dengan lebih banyak operan, yang dengan cepat menjadi jelek dengan tabel kebenaran. Pendekatan lain yang baik adalah peta Karnaugh. Tapi saya tidak akan membahas ini lebih dalam lagi.

deetz
sumber
4

Untuk membuat kode lebih mirip teks, gunakan bendera boolean. Jika logikanya tidak jelas, tambahkan komentar.

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
  {
  ...
  }
anatolyg
sumber
3
if((A && B ) || !A)
{
  //do C
}
else if(!B)
{
  //...
}
Ali
sumber
2

Saya akan mengekstrak C ke suatu metode, dan kemudian keluar dari fungsi sesegera mungkin dalam semua kasus. elseklausa dengan satu hal di akhir harus hampir selalu dibalik jika memungkinkan. Inilah contoh langkah demi langkah:

Ekstrak C:

if (A) {
   if (B)
      C();
   else
      D();
} else
   C();

Balikkan dulu ifuntuk menyingkirkan dulu else:

if (!A) {
   C();
   return;
}

if (B)
   C();
else
   D();

Singkirkan yang kedua else:

if (!A) {
   C();
   return;
}

if (B) {
   C();
   return;
} 

D();

Dan kemudian Anda dapat melihat bahwa kedua kasing memiliki badan yang sama dan dapat digabungkan:

if (!A || B) {
   C();
   return;
}

D();

Hal-hal opsional untuk ditingkatkan adalah:

  • tergantung pada konteks, tetapi jika !A || Bmembingungkan, ekstrak ke satu atau lebih variabel untuk menjelaskan maksud

  • kasus mana pun C()atau D()non-luar biasa harus menjadi yang terakhir, jadi jika D()pengecualian, maka membalikkan yang ifterakhir kalinya

Dave Cousineau
sumber
2

Menggunakan bendera juga dapat mengatasi masalah ini

int flag = 1; 
if ( condition A ) {
    flag = 2;
    if( condition B ) {
        flag = 3;
    }
}
if(flag != 2) { 
    do action C 
}
Spr k
sumber