Cara meningkatkan logika untuk memeriksa apakah 4 nilai boolean cocok dengan beberapa kasus

118

Saya memiliki empat boolnilai:

bool bValue1;
bool bValue2;
bool bValue3;
bool bValue4;

Nilai yang dapat diterima adalah:

         Scenario 1 | Scenario 2 | Scenario 3
bValue1: true       | true       | true
bValue2: true       | true       | false
bValue3: true       | true       | false
bValue4: true       | false      | false

Jadi, misalnya, skenario ini tidak dapat diterima:

bValue1: false
bValue2: true
bValue3: true
bValue4: true

Saat ini saya telah membuat ifpernyataan ini untuk mendeteksi skenario buruk:

if(((bValue4 && (!bValue3 || !bValue2 || !bValue1)) ||
   ((bValue3 && (!bValue2 || !bValue1)) ||
   (bValue2 && !bValue1) ||
   (!bValue1 && !bValue2 && !bValue3 && !bValue4))
{
    // There is some error
}

Bisakah logika pernyataan itu diperbaiki / disederhanakan?

Andrew Truckle
sumber
8
Saya akan menggunakan tabel, bukan ifpernyataan kompleks . Selain itu, karena ini adalah flag boolean, Anda dapat membuat model setiap skenario sebagai konstanta dan memeriksanya.
Zdeslav Vojkovic
3
if (!((bValue1 && bValue2 && bValue3) || (bValue1 && !bValue2 && !bValue3 && !bValue4)))
mch
14
apa skenario sebenarnya? Seringkali segalanya menjadi lebih sederhana jika Anda hanya memberikan nama yang tepat, misalnyabool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
idclev 463035818
6
Dengan menggunakan nama yang bermakna, Anda bisa mengekstrak setiap kondisi kompleks ke dalam metode dan memanggil metode itu dalam kondisi if. Ini akan jauh lebih mudah dibaca dan dipelihara. mis. Lihat contoh yang diberikan di tautan. refactoring.guru/decompose-conditional
Hardik Modha

Jawaban:

195

Saya akan mengincar keterbacaan: Anda hanya memiliki 3 skenario, tangani dengan 3 skenario terpisah:

bool valid = false;
if (bValue1 && bValue2 && bValue3 && bValue4)
    valid = true; //scenario 1
else if (bValue1 && bValue2 && bValue3 && !bValue4)
    valid = true; //scenario 2
else if (bValue1 && !bValue2 && !bValue3 && !bValue4)
    valid = true; //scenario 3

Mudah dibaca dan di-debug, IMHO. Selain itu, Anda dapat menetapkan variabel whichScenariosaat melanjutkan dengan if.

Dengan hanya 3 skenario, saya tidak akan menggunakan sesuatu seperti "jika 3 nilai pertama benar, saya tidak dapat memeriksa nilai keempat": ini akan membuat kode Anda lebih sulit untuk dibaca dan dipelihara.

Bukan solusi yang elegan mungkin tentu saja, tetapi dalam kasus ini tidak apa-apa: mudah dan terbaca.

Jika logika Anda semakin rumit, buang kode itu dan pertimbangkan untuk menggunakan sesuatu yang lebih untuk menyimpan skenario berbeda yang tersedia (seperti yang disarankan Zladeck).

Saya sangat menyukai saran pertama yang diberikan dalam jawaban ini : mudah dibaca, tidak rawan kesalahan, mudah dirawat

(Hampir) di luar topik:

Saya tidak menulis banyak jawaban di sini di StackOverflow. Sungguh lucu bahwa jawaban yang diterima di atas sejauh ini merupakan jawaban yang paling dihargai dalam sejarah saya (tidak pernah mendapat lebih dari 5-10 suara positif sebelumnya), padahal sebenarnya bukan itu yang biasanya saya anggap sebagai cara yang "benar" untuk melakukannya.

Tetapi kesederhanaan sering kali menjadi "cara yang tepat untuk melakukannya", banyak orang tampaknya memikirkan hal ini dan saya harus memikirkannya lebih dari yang saya lakukan :)

Gian Paolo
sumber
1
tentu @hessamhedieh, tidak masalah hanya untuk sejumlah kecil skenario yang tersedia. seperti yang saya katakan, jika segala sesuatunya menjadi lebih rumit, lebih baik cari yang lain
Gian Paolo
4
Ini dapat disederhanakan lebih lanjut dengan menumpuk semua kondisi ke dalam penginisialisasi untuk validdan memisahkannya dengan ||, daripada bermutasi validdalam blok pernyataan terpisah. Saya tidak dapat memberikan contoh di komentar tetapi Anda dapat secara vertikal menyelaraskan ||operator di sepanjang kiri untuk membuatnya sangat jelas; kondisi individu sudah diberi tanda kurung sebanyak yang diperlukan (untuk if) sehingga Anda tidak perlu menambahkan karakter apa pun ke ekspresi selain yang sudah ada.
Leushenko
1
@Leushenko, saya pikir pencampuran tanda kurung, && dan || kondisi cukup rawan error (seseorang di jawaban berbeda mengatakan ada kesalahan dalam tanda kurung di kode di OP, mungkin itu sudah diperbaiki). Penjajaran yang tepat bisa membantu, tentu. Tapi apa keuntungannya? lebih mudah dibaca? lebih mudah dirawat? Saya kira tidak. Hanya pendapat saya, tentu saja. Yang pasti, saya sangat benci memiliki banyak jika dalam kode.
Gian Paolo
3
Saya telah membungkusnya if($bValue1)seperti yang selalu harus benar, secara teknis memungkinkan beberapa peningkatan kinerja kecil (meskipun kita berbicara tentang jumlah yang dapat diabaikan di sini).
Martijn
2
FWIW: hanya ada 2 skenario: 2 yang pertama adalah skenario yang sama dan tidak bergantung padabValue4
Dancrumb
123

Saya bertujuan untuk kesederhanaan dan keterbacaan.

bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
bool scenario2 = bValue1 && bValue2 && bValue3 && !bValue4;
bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4;

if (scenario1 || scenario2 || scenario3) {
    // Do whatever.
}

Pastikan untuk mengganti nama skenario serta nama bendera dengan sesuatu yang deskriptif. Jika masuk akal untuk masalah spesifik Anda, Anda dapat mempertimbangkan alternatif ini:

bool scenario1or2 = bValue1 && bValue2 && bValue3;
bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4;

if (scenario1or2 || scenario3) {
    // Do whatever.
}

Yang penting di sini bukanlah logika predikat. Ini menggambarkan domain Anda dan dengan jelas mengungkapkan maksud Anda. Kuncinya di sini adalah memberikan nama yang baik untuk semua input dan variabel perantara. Jika Anda tidak dapat menemukan nama variabel yang bagus, itu mungkin merupakan tanda bahwa Anda menjelaskan masalah dengan cara yang salah.

Anders
sumber
3
+1 Inilah yang akan saya lakukan juga. Seperti yang ditunjukkan @RedFilter, dan berbeda dengan jawaban yang diterima, ini mendokumentasikan diri sendiri. Memberikan nama skenario mereka sendiri dalam langkah terpisah jauh lebih mudah dibaca.
Andreas
106

Kami dapat menggunakan peta Karnaugh dan mengurangi skenario Anda menjadi persamaan logis. Saya telah menggunakan pemecah peta Karnaugh Online dengan sirkuit untuk 4 variabel.

masukkan deskripsi gambar di sini

Ini menghasilkan:

masukkan deskripsi gambar di sini

Beralih A, B, C, Dke bValue1, bValue2, bValue3, bValue4, ini tidak lain adalah:

bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4

Jadi ifpernyataan Anda menjadi:

if(!(bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4))
{
    // There is some error
}
  • Peta Karnaugh sangat berguna ketika Anda memiliki banyak variabel dan banyak kondisi yang harus dievaluasi true.
  • Setelah mereduksi trueskenario menjadi persamaan logis, menambahkan komentar relevan yang menunjukkan trueskenario adalah praktik yang baik.
PW
sumber
96
Meskipun secara teknis benar, kode ini memerlukan banyak komentar agar dapat diedit oleh pengembang lain beberapa bulan kemudian.
Zdeslav Vojkovic
22
@ZdeslavVojkovic: Saya hanya akan menambahkan komentar dengan persamaan. //!(ABC + AB'C'D') (By K-Map logic). Itu akan menjadi saat yang tepat bagi developer untuk mempelajari K-Maps jika dia belum mengetahuinya.
PW
11
Saya setuju dengan itu, tetapi IMO masalahnya adalah bahwa ia tidak memetakan dengan jelas ke domain masalah, yaitu bagaimana setiap kondisi memetakan ke skenario tertentu yang membuatnya sulit untuk diubah / diperluas. Apa yang terjadi jika ada Edan Fkondisi dan 4 skenario baru? Berapa lama waktu yang dibutuhkan untuk memperbarui ifpernyataan ini dengan benar? Bagaimana tinjauan kode memeriksa apakah itu baik atau tidak? Masalahnya bukan pada sisi teknis tetapi pada sisi "bisnis".
Zdeslav Vojkovic
7
Saya pikir Anda dapat memfaktorkan keluar A: ABC + AB'C'D' = A(BC + B'C'D')(ini bahkan dapat difaktorkan A(B ^ C)'(C + D')meskipun saya akan berhati-hati dengan menyebut 'penyederhanaan' ini).
Maciej Piechotka
28
@PW Komentar itu tampaknya sama mudahnya dengan kode, dan karenanya agak tidak berguna. Komentar yang lebih baik akan menjelaskan bagaimana Anda benar-benar mendapatkan persamaan itu, yaitu bahwa pernyataan tersebut harus memicu TTTT, TTTF dan TFFF. Pada titik itu Anda mungkin juga hanya menulis ketiga kondisi tersebut dalam kode sebagai gantinya dan tidak memerlukan penjelasan sama sekali.
Bernhard Barker
58

Pertanyaan sebenarnya di sini adalah: apa yang terjadi ketika pengembang lain (atau bahkan penulis) harus mengubah kode ini beberapa bulan kemudian.

Saya akan menyarankan pemodelan ini sebagai bendera bit:

const int SCENARIO_1 = 0x0F; // 0b1111 if using c++14
const int SCENARIO_2 = 0x0E; // 0b1110
const int SCENARIO_3 = 0x08; // 0b1000

bool bValue1 = true;
bool bValue2 = false;
bool bValue3 = false;
bool bValue4 = false;

// boolean -> int conversion is covered by standard and produces 0/1
int scenario = bValue1 << 3 | bValue2 << 2 | bValue3 << 1 | bValue4;
bool match = scenario == SCENARIO_1 || scenario == SCENARIO_2 || scenario == SCENARIO_3;
std::cout << (match ? "ok" : "error");

Jika ada lebih banyak skenario atau lebih banyak flag, pendekatan tabel lebih mudah dibaca dan diperluas daripada menggunakan flag. Mendukung skenario baru hanya membutuhkan baris lain dalam tabel.

int scenarios[3][4] = {
    {true, true, true, true},
    {true, true, true, false},
    {true, false, false, false},
};

int main()
{
  bool bValue1 = true;
  bool bValue2 = false;
  bool bValue3 = true;
  bool bValue4 = true;
  bool match = false;

  // depending on compiler, prefer std::size()/_countof instead of magic value of 4
  for (int i = 0; i < 4 && !match; ++i) {
    auto current = scenarios[i];
    match = bValue1 == current[0] && 
            bValue2 == current[1] && 
            bValue3 == current[2] && 
            bValue4 == current[3];
  }

  std::cout << (match ? "ok" : "error");
}
Zdeslav Vojkovic
sumber
4
Bukan yang paling mudah dirawat tetapi pasti menyederhanakan kondisi if. Jadi meninggalkan beberapa komentar seputar operasi bitwise akan menjadi kebutuhan mutlak di sini.
Adam Zahran
6
IMO, tabel adalah pendekatan terbaik karena skalanya lebih baik dengan skenario dan flag tambahan.
Zdeslav Vojkovic
Saya suka solusi pertama Anda, mudah dibaca dan terbuka untuk modifikasi. Saya akan membuat 2 perbaikan: 1: menetapkan nilai ke skenarioX dengan indikasi eksplisit nilai boolean yang digunakan, misalnya SCENARIO_2 = true << 3 | true << 2 | true << 1 | false;2: hindari variabel SCENARIO_X dan kemudian simpan semua skenario yang tersedia di a <std::set<int>. Menambahkan skenario akan menjadi sesuatu yang mySet.insert( true << 3 | false << 2 | true << 1 | false;mungkin sedikit berlebihan hanya untuk 3 skenario, OP menerima solusi cepat, kotor dan mudah yang saya sarankan dalam jawaban saya.
Gian Paolo
4
Jika Anda menggunakan C ++ 14 atau lebih tinggi, saya sarankan untuk menggunakan literal biner untuk solusi pertama - 0b1111, 0b1110 dan 0b1000 jauh lebih jelas. Anda mungkin juga dapat menyederhanakan ini sedikit menggunakan pustaka standar ( std::find?).
Bernhard Barker
2
Saya menemukan bahwa literal biner di sini akan menjadi persyaratan minimal untuk membuat kode pertama bersih. Dalam bentuknya saat ini, ini benar-benar samar. Pengidentifikasi deskriptif mungkin membantu tetapi saya bahkan tidak yakin tentang itu. Faktanya, operasi bit untuk menghasilkan scenarionilai menurut saya rawan kesalahan yang tidak perlu.
Konrad Rudolph
27

Jawaban saya sebelumnya sudah merupakan jawaban yang diterima, saya menambahkan sesuatu di sini yang menurut saya dapat dibaca, mudah dan dalam hal ini terbuka untuk modifikasi di masa mendatang:

Dimulai dengan jawaban @ZdeslavVojkovic (yang menurut saya cukup bagus), saya menemukan ini:

#include <iostream>
#include <set>

//using namespace std;

int GetScenarioInt(bool bValue1, bool bValue2, bool bValue3, bool bValue4)
{
    return bValue1 << 3 | bValue2 << 2 | bValue3 << 1 | bValue4;
}
bool IsValidScenario(bool bValue1, bool bValue2, bool bValue3, bool bValue4)
{
    std::set<int> validScenarios;
    validScenarios.insert(GetScenarioInt(true, true, true, true));
    validScenarios.insert(GetScenarioInt(true, true, true, false));
    validScenarios.insert(GetScenarioInt(true, false, false, false));

    int currentScenario = GetScenarioInt(bValue1, bValue2, bValue3, bValue4);

    return validScenarios.find(currentScenario) != validScenarios.end();
}

int main()
{
    std::cout << IsValidScenario(true, true, true, false) << "\n"; // expected = true;
    std::cout << IsValidScenario(true, true, false, false) << "\n"; // expected = false;

    return 0;
}

Lihat cara kerjanya di sini

Nah, itulah solusi "elegan dan dapat dipelihara" (IMHO) yang biasanya saya tuju, tetapi sungguh, untuk kasus OP, jawaban "seandainya" saya sebelumnya lebih cocok dengan persyaratan OP, meskipun tidak elegan atau dapat dipelihara.

Gian Paolo
sumber
Anda tahu bahwa Anda selalu dapat mengedit jawaban sebelumnya dan melakukan perbaikan.
Andreas
20

Saya juga ingin mengajukan pendekatan lain.

Ide saya adalah mengubah bools menjadi integer dan kemudian membandingkannya menggunakan template variadic:

unsigned bitmap_from_bools(bool b) {
    return b;
}
template<typename... args>
unsigned bitmap_from_bools(bool b, args... pack) {
    return (bitmap_from_bools(b) << sizeof...(pack)) | bitmap_from_bools(pack...);
}

int main() {
    bool bValue1;
    bool bValue2;
    bool bValue3;
    bool bValue4;

    unsigned summary = bitmap_from_bools(bValue1, bValue2, bValue3, bValue4);

    if (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u) {
        //bad scenario
    }
}

Perhatikan bagaimana sistem ini dapat mendukung hingga 32 bools sebagai input. mengganti unsigneddengan unsigned long long(atau uint64_t) meningkatkan dukungan ke 64 kasus. Jika Anda tidak menyukainya if (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u), Anda juga bisa menggunakan metode templat variadic lain:

bool equals_any(unsigned target, unsigned compare) {
    return target == compare;
}
template<typename... args>
bool equals_any(unsigned target, unsigned compare, args... compare_pack) {
    return equals_any(target, compare) ? true : equals_any(target, compare_pack...);
}

int main() {
    bool bValue1;
    bool bValue2;
    bool bValue3;
    bool bValue4;

    unsigned summary = bitmap_from_bools(bValue1, bValue2, bValue3, bValue4);

    if (!equals_any(summary, 0b1111u, 0b1110u, 0b1000u)) {
        //bad scenario
    }
}
Stack Danny
sumber
2
Saya suka pendekatan ini, kecuali untuk nama fungsi utama: “dari bool… ke apa ?” - Mengapa tidak secara eksplisit bitmap_from_bools,, atau bools_to_bitmap?
Konrad Rudolph
ya @KonradRudol, saya tidak bisa memikirkan nama yang lebih baik, kecuali mungkin bools_to_unsigned. Bitmap adalah kata kunci yang bagus; diedit.
Stack Danny
Saya pikir Anda mau summary!= 0b1111u &&.... a != b || a != cselalu benar jikab != c
MooseBoys
17

Berikut versi yang disederhanakan:

if (bValue1 && (bValue2 == bValue3) && (bValue2 || !bValue4)) {
    // acceptable
} else {
    // not acceptable
}

Perhatikan, tentu saja, solusi ini lebih kabur daripada yang asli, artinya mungkin lebih sulit untuk dipahami.


Pembaruan: MSalters di komentar menemukan ekspresi yang lebih sederhana:

if (bValue1&&(bValue2==bValue3)&&(bValue2>=bValue4)) ...
geza
sumber
1
Ya, tapi sulit dimengerti. Tapi terima kasih atas sarannya.
Andrew Truckle
Saya membandingkan kemampuan kompiler untuk menyederhanakan ekspresi dengan penyederhanaan Anda sebagai referensi: penjelajah kompiler . gcc tidak menemukan versi optimal Anda tetapi solusinya masih bagus. Clang dan MSVC sepertinya tidak melakukan penyederhanaan ekspresi boolean.
Oliv
1
@AndrewTruckle: perhatikan, jika Anda membutuhkan versi yang lebih mudah dibaca, harap katakan. Anda telah mengatakan "disederhanakan", namun Anda menerima versi yang lebih panjang daripada yang asli.
geza
1
simplememang istilah yang tidak jelas. Banyak orang memahaminya dalam konteks ini karena lebih sederhana bagi pengembang untuk memahami dan bukan bagi kompiler untuk menghasilkan kode, jadi lebih banyak verbose memang bisa lebih sederhana.
Zdeslav Vojkovic
1
@IsmaelMiguel: ketika rumus logika dioptimalkan untuk jumlah istilah, arti aslinya biasanya hilang. Tetapi seseorang dapat memberi komentar di sekitarnya, jadi jelas apa yang dilakukannya. Bahkan, untuk jawaban yang diterima, komentar tidak akan merugikan.
geza
12

Pertimbangkan untuk menerjemahkan tabel Anda langsung ke dalam program Anda. Dorong program berdasarkan tabel, bukan meniru dengan logika.

template<class T0>
auto is_any_of( T0 const& t0, std::initializer_list<T0> il ) {
  for (auto&& x:il)
    if (x==t0) return true;
  return false;
}

sekarang

if (is_any_of(
  std::make_tuple(bValue1, bValue2, bValue3, bValue4),
  {
    {true, true, true, true},
    {true, true, true, false},
    {true, false, false, false}
  }
))

ini secara langsung mungkin mengkodekan tabel kebenaran Anda ke dalam kompiler.

Contoh langsung .

Anda juga bisa menggunakan std::any_ofsecara langsung:

using entry = std::array<bool, 4>;
constexpr entry acceptable[] = 
  {
    {true, true, true, true},
    {true, true, true, false},
    {true, false, false, false}
  };
if (std::any_of( begin(acceptable), end(acceptable), [&](auto&&x){
  return entry{bValue1, bValue2, bValue3, bValue4} == x;
}) {
}

kompilator dapat menyebariskan kode, dan menghilangkan iterasi apa pun serta membangun logikanya sendiri untuk Anda. Sementara itu, kode Anda mencerminkan dengan tepat bagaimana Anda memahami masalahnya.

Yakk - Adam Nevraumont
sumber
Versi pertama sangat mudah dibaca dan dapat dirawat, saya sangat menyukainya. Yang kedua lebih sulit untuk dibaca, setidaknya bagi saya, dan membutuhkan tingkat keterampilan c ++ mungkin di atas rata-rata, pasti melebihi saya. Bukan sesuatu yang bisa ditulis semua orang. Baru saja belajar sesuatu yang baru, terima kasih
Gian Paolo
11

Saya hanya memberikan jawaban saya di sini seperti di komentar yang disarankan seseorang untuk menunjukkan solusi saya. Saya ingin berterima kasih kepada semua orang atas wawasan mereka.

Pada akhirnya saya memilih untuk menambahkan tiga booleanmetode "skenario" baru:

bool CChristianLifeMinistryValidationDlg::IsFirstWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
    return (INCLUDE_ITEM1(pEntry) && 
           !INCLUDE_ITEM2(pEntry) && 
           !INCLUDE_ITEM3(pEntry) && 
           !INCLUDE_ITEM4(pEntry));
}

bool CChristianLifeMinistryValidationDlg::IsSecondWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
    return (INCLUDE_ITEM1(pEntry) &&
            INCLUDE_ITEM2(pEntry) &&
            INCLUDE_ITEM3(pEntry) &&
            INCLUDE_ITEM4(pEntry));
}

bool CChristianLifeMinistryValidationDlg::IsOtherWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
    return (INCLUDE_ITEM1(pEntry) && 
            INCLUDE_ITEM2(pEntry) && 
            INCLUDE_ITEM3(pEntry) && 
           !INCLUDE_ITEM4(pEntry));
}

Kemudian saya dapat menerapkannya secara rutin validasi saya seperti ini:

if (!IsFirstWeekStudentItems(pEntry) && !IsSecondWeekStudentItems(pEntry) && !IsOtherWeekStudentItems(pEntry))
{
    ; Error
}

Dalam aplikasi langsung saya, 4 nilai bool sebenarnya diekstrak dari DWORDyang memiliki 4 nilai yang dikodekan ke dalamnya.

Terima kasih sekali lagi semuanya.

Andrew Truckle
sumber
1
Terima kasih telah membagikan solusinya. :) Sebenarnya lebih baik dari yang kompleks kalo kondisinya sih. Mungkin Anda masih bisa memberi nama INCLUDE_ITEM1dll dengan cara yang lebih baik dan Anda semua baik-baik saja. :)
Hardik Modha
1
@HardikModha Ya, secara teknis mereka adalah "Item siswa" dan benderanya adalah untuk menunjukkan apakah mereka akan "disertakan". Jadi menurut saya namanya, meskipun terdengar umum, sebenarnya memiliki arti dalam konteks ini. :)
Andrew Truckle
11

Saya tidak melihat jawaban yang mengatakan untuk menamai skenario, meskipun solusi OP melakukan hal itu.

Bagi saya, yang terbaik adalah merangkum komentar tentang setiap skenario menjadi nama variabel atau nama fungsi. Anda lebih cenderung mengabaikan komentar daripada nama, dan jika logika Anda berubah di masa mendatang, Anda kemungkinan besar akan mengubah nama daripada komentar. Anda tidak dapat mengubah komentar.

Jika Anda berencana untuk menggunakan kembali skenario ini di luar fungsi Anda (atau mungkin menginginkannya), buatlah fungsi yang mengatakan apa yang dievaluasi ( constexpr/ noexceptopsional tetapi disarankan):

constexpr bool IsScenario1(bool b1, bool b2, bool b3, bool b4) noexcept
{ return b1 && b2 && b3 && b4; }

constexpr bool IsScenario2(bool b1, bool b2, bool b3, bool b4) noexcept
{ return b1 && b2 && b3 && !b4; }

constexpr bool IsScenario3(bool b1, bool b2, bool b3, bool b4) noexcept
{ return b1 && !b2 && !b3 && !b4; }

Buat metode kelas ini jika memungkinkan (seperti dalam solusi OP). Anda dapat menggunakan variabel di dalam fungsi Anda jika menurut Anda Anda tidak akan menggunakan kembali logika:

const auto is_scenario_1 = bValue1 && bValue2 && bValue3 && bValue4;
const auto is_scenario_2 = bvalue1 && bvalue2 && bValue3 && !bValue4;
const auto is_scenario_3 = bValue1 && !bValue2 && !bValue3 && !bValue4;

Kompilator kemungkinan besar akan memilah bahwa jika bValue1 salah, maka semua skenario salah. Jangan khawatir tentang membuatnya cepat, cukup benar dan mudah dibaca. Jika Anda membuat profil kode Anda dan menemukan ini sebagai penghambat karena compiler menghasilkan kode sub-optimal pada -O2 atau lebih tinggi, maka cobalah untuk menulis ulang.

Keliru
sumber
Saya suka ini sedikit lebih dari solusi Gian Paolo (sudah bagus): Ini menghindari aliran kontrol dan penggunaan variabel yang ditimpa - gaya yang lebih fungsional.
Dirk Herrmann
9

Cara AC / C ++

bool scenario[3][4] = {{true, true, true, true}, 
                        {true, true, true, false}, 
                        {true, false, false, false}};

bool CheckScenario(bool bValue1, bool bValue2, bool bValue3, bool bValue4)
{
    bool temp[] = {bValue1, bValue2, bValue3, bValue4};
    for(int i = 0 ; i < sizeof(scenario) / sizeof(scenario[0]); i++)
    {
        if(memcmp(temp, scenario[i], sizeof(temp)) == 0)
            return true;
    }
    return false;
}

Pendekatan ini dapat diskalakan seolah-olah jumlah kondisi yang valid bertambah, Anda dengan mudah menambahkan lebih banyak dari mereka ke daftar skenario.

hessam hedieh
sumber
Saya cukup yakin ini salah. Ini mengasumsikan bahwa kompilator hanya menggunakan satu representasi biner untuk true. Kompiler yang menggunakan "apa pun yang bukan nol adalah benar" menyebabkan kode ini gagal. Perhatikan bahwa trueharus dikonversi ke 1, hanya saja tidak perlu disimpan seperti itu.
MSalters
@ MSalters, tnx, saya mengerti maksud Anda dan saya menyadari itu, agak seperti 2 is not equal to true but evaluates to true, kode saya tidak memaksa int 1 = truedan berfungsi selama semua yang benar diubah ke nilai int yang sama, JADI inilah pertanyaan saya: Mengapa kompiler harus bertindak acak saat mengonversi sesuai dengan int yang mendasari, Bisakah Anda menjelaskan lebih lanjut?
hessam hedieh
Melakukan memcmpuntuk menguji kondisi boolean bukanlah cara C ++, dan saya agak meragukan bahwa itu juga cara C yang mapan.
Konrad Rudolph
@hessamhedieh: Masalah dalam logika Anda adalah "mengubah true menjadi int yang mendasari". Itu bukan cara kerja kompiler,
MSalters
Kode Anda meningkatkan kompleksitas dari O (1) ke O (n). Bukan cara untuk menggunakan bahasa apa pun - tinggalkan C / C ++.
mabel
9

Sangat mudah untuk melihat bahwa dua skenario pertama serupa - mereka memiliki sebagian besar kondisi yang sama. Jika Anda ingin memilih di skenario mana Anda saat ini, Anda dapat menulisnya seperti ini (ini adalah solusi @ gian-paolo yang dimodifikasi ):

bool valid = false;
if(bValue1 && bValue2 && bValue3)
{
    if (bValue4)
        valid = true; //scenario 1
    else if (!bValue4)
        valid = true; //scenario 2
}
else if (bValue1 && !bValue2 && !bValue3 && !bValue4)
    valid = true; //scenario 3

Lebih jauh lagi, Anda dapat memperhatikan, bahwa boolean pertama harus selalu benar, yang merupakan kondisi entri, sehingga Anda bisa mendapatkan:

bool valid = false;
if(bValue1)
{
    if(bValue2 && bValue3)
    {
        if (bValue4)
            valid = true; //scenario 1
        else if (!bValue4)
            valid = true; //scenario 2
    }
    else if (!bValue2 && !bValue3 && !bValue4)
        valid = true; //scenario 3
}

Terlebih lagi, Anda sekarang dapat melihat dengan jelas, bahwa bValue2 dan bValue3 agak terhubung - Anda dapat mengekstrak statusnya ke beberapa fungsi atau variabel eksternal dengan nama yang lebih sesuai (ini tidak selalu mudah atau sesuai):

bool valid = false;
if(bValue1)
{
    bool bValue1and2 = bValue1 && bValue2;
    bool notBValue1and2 = !bValue2 && !bValue3;
    if(bValue1and2)
    {
        if (bValue4)
            valid = true; //scenario 1
        else if (!bValue4)
            valid = true; //scenario 2
    }
    else if (notBValue1and2 && !bValue4)
        valid = true; //scenario 3
}

Melakukannya dengan cara ini memiliki beberapa keuntungan dan kerugian:

  • kondisi lebih kecil, jadi lebih mudah untuk bernalar tentang mereka,
  • lebih mudah untuk melakukan penggantian nama yang bagus untuk membuat ketentuan ini lebih mudah dipahami,
  • tetapi, mereka perlu memahami ruang lingkup,
  • apalagi itu lebih kaku

Jika Anda memprediksi bahwa akan ada perubahan pada logika di atas, Anda harus menggunakan pendekatan yang lebih langsung seperti yang disajikan oleh @ gian-paolo .

Jika tidak, jika ketentuan ini ditetapkan dengan baik, dan merupakan jenis "aturan solid" yang tidak akan pernah berubah, pertimbangkan cuplikan kode terakhir saya.

Michał Łoś
sumber
7

Seperti yang disarankan oleh mch, Anda dapat melakukan:

if(!((bValue1 && bValue2 && bValue3) || 
  (bValue1 && !bValue2 && !bValue3 && !bValue4))
)

di mana baris pertama mencakup dua kasus bagus pertama, dan baris kedua mencakup yang terakhir.

Live Demo, di mana saya bermain-main dan melewati kasus Anda.

gsamaras.dll
sumber
7

Sedikit variasi dari jawaban bagus @ GianPaolo, yang mungkin lebih mudah dibaca oleh beberapa orang:

bool any_of_three_scenarios(bool v1, bool v2, bool v3, bool v4)
{
  return (v1 &&  v2 &&  v3 &&  v4)  // scenario 1
      || (v1 &&  v2 &&  v3 && !v4)  // scenario 2
      || (v1 && !v2 && !v3 && !v4); // scenario 3
}

if (any_of_three_scenarios(bValue1,bValue2,bValue3,bValue4))
{
  // ...
}
Matt
sumber
7

Setiap jawaban terlalu rumit dan sulit dibaca. Solusi terbaik untuk ini adalah switch()pernyataan. Ini dapat dibaca dan mempermudah penambahan / modifikasi kasus tambahan. Kompiler juga pandai mengoptimalkan switch()pernyataan.

switch( (bValue4 << 3) | (bValue3 << 2) | (bValue2 << 1) | (bValue1) )
{
    case 0b1111:
        // scenario 1
        break;

    case 0b0111:
        // scenario 2
        break;

    case 0b0001:
        // scenario 3
        break;

    default:
        // fault condition
        break;
}

Anda tentu saja dapat menggunakan konstanta dan OR bersama-sama dalam casepernyataan agar lebih mudah dibaca.

tersendat
sumber
Menjadi C-programmer lama, saya akan mendefinisikan makro "PackBools" dan menggunakannya untuk "switch (PackBools (a, b, c, d))" dan untuk kasus, misalnya secara langsung "case PackBools (true , true ...) "atau definisikan mereka sebagai constants.eg lokal" const unsigned int scenario1 = PackBools (true, true ...); "
Simon F
6

Saya juga akan menggunakan variabel pintas untuk kejelasan. Seperti disebutkan sebelumnya, skenario 1 sama dengan skenario 2, karena nilai bValue4 tidak memengaruhi kebenaran kedua skenario tersebut.

bool MAJORLY_TRUE=bValue1 && bValue2 && bValue3
bool MAJORLY_FALSE=!(bValue2 || bValue3 || bValue4)

maka ekspresi Anda menjadi:

if (MAJORLY_TRUE || (bValue1 && MAJORLY_FALSE))
{
     // do something
}
else
{
    // There is some error
}

Memberi nama yang bermakna untuk variabel MAJORTRUE dan MAJORFALSE (serta sebenarnya ke bValue * vars) akan sangat membantu dengan keterbacaan dan pemeliharaan.

Gnudiff
sumber
6

Fokus pada keterbacaan masalah, bukan pernyataan "jika" yang spesifik.

Meskipun ini akan menghasilkan lebih banyak baris kode, dan beberapa mungkin menganggapnya berlebihan atau tidak perlu. Saya menyarankan bahwa mengabstraksi skenario Anda dari boolean tertentu adalah cara terbaik untuk menjaga keterbacaan.

Dengan memecah berbagai hal menjadi beberapa kelas (jangan ragu untuk hanya menggunakan fungsi, atau alat lain apa pun yang Anda sukai) dengan nama yang dapat dimengerti - kita dapat dengan lebih mudah menunjukkan arti di balik setiap skenario. Lebih penting lagi, dalam sistem dengan banyak bagian yang bergerak - lebih mudah untuk memelihara dan bergabung ke dalam sistem Anda yang ada (sekali lagi, terlepas dari banyaknya kode tambahan yang dimasukkan).

#include <iostream>
#include <vector>
using namespace std;

// These values would likely not come from a single struct in real life
// Instead, they may be references to other booleans in other systems
struct Values
{
    bool bValue1; // These would be given better names in reality
    bool bValue2; // e.g. bDidTheCarCatchFire
    bool bValue3; // and bDidTheWindshieldFallOff
    bool bValue4;
};

class Scenario
{
public:
    Scenario(Values& values)
    : mValues(values) {}

    virtual operator bool() = 0;

protected:
    Values& mValues;    
};

// Names as examples of things that describe your "scenarios" more effectively
class Scenario1_TheCarWasNotDamagedAtAll : public Scenario
{
public:
    Scenario1_TheCarWasNotDamagedAtAll(Values& values) : Scenario(values) {}

    virtual operator bool()
    {
        return mValues.bValue1
        && mValues.bValue2
        && mValues.bValue3
        && mValues.bValue4;
    }
};

class Scenario2_TheCarBreaksDownButDidntGoOnFire : public Scenario
{
public:
    Scenario2_TheCarBreaksDownButDidntGoOnFire(Values& values) : Scenario(values) {}

    virtual operator bool()
    {
        return mValues.bValue1
        && mValues.bValue2
        && mValues.bValue3
        && !mValues.bValue4;
    }   
};

class Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere : public Scenario
{
public:
    Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere(Values& values) : Scenario(values) {}

    virtual operator bool()
    {
        return mValues.bValue1
        && !mValues.bValue2
        && !mValues.bValue3
        && !mValues.bValue4;
    }   
};

Scenario* findMatchingScenario(std::vector<Scenario*>& scenarios)
{
    for(std::vector<Scenario*>::iterator it = scenarios.begin(); it != scenarios.end(); it++)
    {
        if (**it)
        {
            return *it;
        }
    }
    return NULL;
}

int main() {
    Values values = {true, true, true, true};
    std::vector<Scenario*> scenarios = {
        new Scenario1_TheCarWasNotDamagedAtAll(values),
        new Scenario2_TheCarBreaksDownButDidntGoOnFire(values),
        new Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere(values)
    };

    Scenario* matchingScenario = findMatchingScenario(scenarios);

    if(matchingScenario)
    {
        std::cout << matchingScenario << " was a match" << std::endl;
    }
    else
    {
        std::cout << "No match" << std::endl;
    }

    // your code goes here
    return 0;
}

sumber
5
Pada titik tertentu, verbositas mulai merusak keterbacaan. Saya pikir ini sudah keterlaluan.
JollyJoker
2
@JollyJoker Saya benar-benar setuju dalam situasi khusus ini - namun, firasat saya dari cara OP menamai semuanya dengan sangat umum, adalah bahwa kode "asli" mereka kemungkinan jauh lebih kompleks daripada contoh yang mereka berikan. Sungguh, saya hanya ingin meletakkan alternatif ini di luar sana, karena begitulah cara saya menyusunnya untuk sesuatu yang jauh lebih kompleks / terlibat. Tapi Anda benar - untuk contoh spesifik OP, itu terlalu bertele-tele dan memperburuk keadaan.
5

Itu tergantung pada apa yang mereka wakili.

Misalnya jika 1 adalah kunci, dan 2 dan 3 adalah dua orang yang harus setuju (kecuali jika mereka setuju NOTmereka membutuhkan orang ketiga - 4 - untuk mengonfirmasi) yang paling mudah dibaca mungkin:

1 &&
    (
        (2 && 3)   
        || 
        ((!2 && !3) && !4)
    )

dengan permintaan populer:

Key &&
    (
        (Alice && Bob)   
        || 
        ((!Alice && !Bob) && !Charlie)
    )
ispiro
sumber
2
Anda mungkin benar, tetapi menggunakan angka untuk mengilustrasikan poin Anda mengurangi jawaban Anda. Coba gunakan nama deskriptif.
jxh
1
@jxh Itulah angka-angka OP yang digunakan. Saya baru saja menghapus bValue.
ispiro
@jxh Saya harap sekarang lebih baik.
ispiro
4

Melakukan operasi bitwise terlihat sangat bersih dan mudah dipahami.

int bitwise = (bValue4 << 3) | (bValue3 << 2) | (bValue2 << 1) | (bValue1);
if (bitwise == 0b1111 || bitwise == 0b0111 || bitwise == 0b0001)
{
    //satisfying condition
}
Derviş Kayımbaşıoğlu
sumber
1
Perbandingan bitwise terlihat dapat dibaca oleh saya. Komposisi, di sisi lain, terlihat artifisial.
xtofl
3

Saya menunjukkan a, b, c, d untuk kejelasan, dan A, B, C, D untuk pelengkap

bValue1 = a (!A)
bValue2 = b (!B)
bValue3 = c (!C)
bValue4 = d (!D)

Persamaan

1 = abcd + abcD + aBCD
  = a (bcd + bcD + BCD)
  = a (bc + BCD)
  = a (bcd + D (b ^C))

Gunakan persamaan apa pun yang cocok untuk Anda.

mabel
sumber
3
If (!bValue1 || (bValue2 != bValue3) || (!bValue4 && bValue2))
{
// you have a problem
}
  • b1 harus selalu benar
  • b2 harus selalu sama dengan b3
  • dan b4 tidak bisa salah jika b2 (dan b3) benar

sederhana

Owen Meyer
sumber
3

Hanya preferensi pribadi atas jawaban yang diterima, tetapi saya akan menulis:

bool valid = false;
// scenario 1
valid = valid || (bValue1 && bValue2 && bValue3 && bValue4);
// scenario 2
valid = valid || (bValue1 && bValue2 && bValue3 && !bValue4);
// scenario 3
valid = valid || (bValue1 && !bValue2 && !bValue3 && !bValue4);
François Gueguen
sumber
2

Pertama, dengan asumsi Anda hanya dapat mengubah pemeriksaan skenario, saya akan fokus pada keterbacaan dan hanya membungkus pemeriksaan dalam fungsi sehingga Anda dapat memanggil if(ScenarioA()).


Sekarang, dengan asumsi Anda benar-benar ingin / perlu mengoptimalkan ini, saya akan merekomendasikan untuk mengubah Boolean yang terhubung erat menjadi bilangan bulat konstan, dan menggunakan operator bit pada mereka.

public class Options {
  public const bool A = 2; // 0001
  public const bool B = 4; // 0010
  public const bool C = 16;// 0100
  public const bool D = 32;// 1000
//public const bool N = 2^n; (up to n=32)
}

...

public isScenario3(int options) {
  int s3 = Options.A | Options.B | Options.C;
  // for true if only s3 options are set
  return options == s3;
  // for true if s3 options are set
  // return options & s3 == s3
}

Hal ini membuat pengungkapan skenario semudah membuat daftar apa yang menjadi bagian darinya, memungkinkan Anda menggunakan pernyataan switch untuk melompat ke kondisi yang benar, dan membingungkan sesama pengembang yang belum pernah melihat ini sebelumnya. (C # RegexOptions menggunakan pola ini untuk menyetel bendera, saya tidak tahu apakah ada contoh pustaka c ++)

Tezra
sumber
Sebenarnya saya tidak menggunakan empat nilai bool tetapi DWORD dengan empat BOOLS tertanam. Terlambat untuk mengubahnya sekarang. Tapi terima kasih atas saran Anda.
Andrew Truckle
2

Sarang ifmungkin lebih mudah dibaca bagi sebagian orang. Ini versi saya

bool check(int bValue1, int bValue2, int bValue3, int bValue4)
{
  if (bValue1)
  {
    if (bValue2)
    {
      // scenario 1-2
      return bValue3;
    }
    else
    {
      // scenario 3
      return !bValue3 && !bValue4;
    }
  }

  return false;
}
sardok
sumber
Secara pribadi, saya biasanya menghindari pernyataan bersarang jika memungkinkan. Meskipun kasus ini bagus dan mudah dibaca, begitu kemungkinan baru ditambahkan, penyarangan bisa menjadi sangat sulit untuk dibaca. Tetapi jika skenario tidak pernah berubah, itu pasti adalah solusi yang bagus dan mudah dibaca.
Dnomyar96
@ Dnomyar96 saya setuju. Saya pribadi menghindari jika bersarang juga. Terkadang jika logikanya rumit, lebih mudah bagi saya untuk memahami logika dengan memecahnya menjadi beberapa bagian. Misalnya, setelah Anda memasukkan bValue1blok, maka Anda dapat memperlakukan semua yang ada di dalamnya sebagai halaman baru dalam proses mental Anda. Saya yakin cara mendekati masalah mungkin sangat pribadi atau bahkan budaya.
sardok
1

Beberapa jawaban yang benar telah diberikan untuk pertanyaan ini, tetapi saya akan mengambil pandangan berbeda: jika kodenya terlihat terlalu rumit, ada sesuatu yang tidak beres . Kode akan sulit untuk di-debug dan lebih cenderung menjadi "hanya sekali pakai".

Dalam kehidupan nyata, saat kita menemukan situasi seperti ini:

         Scenario 1 | Scenario 2 | Scenario 3
bValue1: true       | true       | true
bValue2: true       | true       | false
bValue3: true       | true       | false
bValue4: true       | false      | false

Ketika empat keadaan dihubungkan oleh pola yang begitu tepat, kita berurusan dengan konfigurasi beberapa "entitas" dalam model kita .

Metafora ekstrim adalah bagaimana kita menggambarkan "manusia" dalam sebuah model, jika kita tidak menyadari keberadaan mereka sebagai entitas kesatuan dengan komponen yang terhubung ke derajat kebebasan tertentu: kita harus mendeskripsikan keadaan independen dari "batang tubuh", "lengan", "kaki", dan "kepala" yang akan membuatnya rumit untuk memahami sistem yang dijelaskan. Hasil langsungnya adalah ekspresi boolean yang rumit dan tidak wajar.

Jelas, cara untuk mengurangi kompleksitas adalah abstraksi dan alat pilihan di c ++ adalah paradigma objek .

Jadi pertanyaannya adalah: mengapa ada pola seperti itu? Apa ini dan apa yang diwakilinya?

Karena kita tidak tahu jawabannya, kita dapat kembali ke abstraksi matematika: array : kita memiliki tiga skenario, yang masing-masing sekarang adalah sebuah array.

                0   1   2   3
Scenario 1:     T   T   T   T
Scenario 2:     T   T   T   F
Scenario 3:     T   F   F   F

Pada titik mana Anda memiliki konfigurasi awal Anda. sebagai sebuah array. Misalnyastd::array memiliki operator persamaan:

Pada titik mana sintaks Anda menjadi:

if( myarray == scenario1 ) {
  // arrays contents are the same

} 
else if ( myarray == scenario2 ) {
  // arrays contents are the same

} 

else if ( myarray == scenario3 ) {
  // arrays contents are the same

} 
else {
  // not the same

}

Seperti jawaban Gian Paolo yang singkat, jelas dan mudah diverifikasi / debuggable. Dalam kasus ini, kami telah mendelegasikan detail ekspresi boolean ke kompilator.

fralau
sumber
1

Anda tidak perlu khawatir tentang kombinasi flag boolean yang tidak valid jika Anda menyingkirkan flag boolean.

Nilai yang dapat diterima adalah:

         Scenario 1 | Scenario 2 | Scenario 3
bValue1: true       | true       | true
bValue2: true       | true       | false
bValue3: true       | true       | false
bValue4: true       | false      | false

Anda jelas memiliki tiga keadaan (skenario). Akan lebih baik untuk memodelkannya dan mendapatkan properti boolean dari status tersebut, bukan sebaliknya.

enum State
{
    scenario1,
    scenario2,
    scenario3,
};

inline bool isValue1(State s)
{
    // (Well, this is kind of silly.  Do you really need this flag?)
    return true;
}

inline bool isValue2(State s)
{
    switch (s)
    {
        case scenario1:
        case scenario2:
            return true;
        case scenario3:
            return false;
    }
}

inline bool isValue3(State s)
{
    // (This is silly too.  Do you really need this flag?)
    return isValue2(s);
}

inline bool isValue4(State s)
{
    switch (s)
    {
        case scenario1:
            return true;
        case scenario2:
        case scenario3:
            return false;
    }
}

Ini jelas lebih banyak kode daripada dalam jawaban Gian Paolo , tetapi tergantung pada situasi Anda, ini bisa jauh lebih bisa dipertahankan:

  • Ada sekumpulan fungsi pusat untuk diubah jika properti atau skenario boolean tambahan ditambahkan.
    • Menambahkan properti hanya membutuhkan satu fungsi.
    • Jika menambahkan skenario, mengaktifkan peringatan kompilator tentang enumkasus yang tidak tertangani dalam switchpernyataan akan menangkap pengambil properti yang tidak menangani skenario itu.
  • Jika Anda perlu mengubah properti boolean secara dinamis, Anda tidak perlu memvalidasi ulang kombinasinya di mana pun. Alih-alih mengganti flag boolean individual (yang dapat mengakibatkan kombinasi flag yang tidak valid), Anda malah akan memiliki mesin status yang bertransisi dari satu skenario ke skenario lainnya.

Pendekatan ini juga memiliki keuntungan sampingan karena sangat efisien.

jamesdlin.dll
sumber
0

2 sen saya: menyatakan jumlah variabel (integer) sehingga

if(bValue1)
{
  sum=sum+1;
}
if(bValue2)
{
  sum=sum+2;
}
if(bValue3)
{
  sum=sum+4;
}
if(bValue4)
{
  sum=sum+8;
}

Periksa jumlah terhadap kondisi yang Anda inginkan dan hanya itu. Dengan cara ini Anda dapat menambahkan lebih banyak ketentuan dengan mudah di masa mendatang, membuatnya tetap mudah dibaca.

SCdev
sumber
0

Jawaban yang diterima baik-baik saja jika Anda hanya memiliki 3 kasus, dan logika untuk masing-masing kasus tersebut sederhana.

Tetapi jika logika untuk setiap kasus lebih rumit, atau ada lebih banyak kasus, opsi yang jauh lebih baik adalah menggunakan rantai tanggung jawab. pola desain .

Anda membuat BaseValidatoryang berisi referensi ke a BaseValidatordan metode ke validatedan metode untuk memanggil validasi pada validator yang direferensikan.

class BaseValidator {
    BaseValidator* nextValidator;

    public:
    BaseValidator() {
        nextValidator = 0;
    }

    void link(BaseValidator validator) {
        if (nextValidator) {
            nextValidator->link(validator);
        } else {
            nextValidator = validator;
        }
    }

    bool callLinkedValidator(bool v1, bool v2, bool v3, bool v4) {
        if (nextValidator) {
            return nextValidator->validate(v1, v2, v3, v4);
        }

        return false;
    }

    virtual bool validate(bool v1, bool v2, bool v3, bool v4) {
        return false;
    }
}

Kemudian Anda membuat sejumlah subclass yang diwarisi dari BaseValidator, mengganti validatemetode dengan logika yang diperlukan untuk setiap validator.

class Validator1: public BaseValidator {
    public:
    bool validate(bool v1, bool v2, bool v3, bool v4) {
        if (v1 && v2 && v3 && v4) {
            return true;
        }

        return nextValidator->callLinkedValidator(v1, v2, v3, v4);
    }
}

Kemudian menggunakannya sederhana, buat instance masing-masing validator Anda, dan setel masing-masing menjadi root yang lain:

Validator1 firstValidator = new Validator1();
Validator2 secondValidator = new Validator2();
Validator3 thirdValidator = new Validator3();
firstValidator.link(secondValidator);
firstValidator.link(thirdValidator);
if (firstValidator.validate(value1, value2, value3, value4)) { ... }

Intinya, setiap kasus validasi memiliki kelasnya sendiri yang bertanggung jawab untuk (a) menentukan apakah validasi cocok dengan itu terjadi, dan (b) mengirimkan validasi untuk orang lain dalam rantai jika tidak.

Harap dicatat bahwa saya tidak terbiasa dengan C ++. Saya sudah mencoba mencocokkan sintaks dari beberapa contoh yang saya temukan online, tetapi jika ini tidak berhasil, perlakukan lebih seperti kodesemu. Saya juga memiliki contoh Python yang berfungsi lengkap di bawah ini yang dapat digunakan sebagai dasar jika diinginkan.

class BaseValidator:
    def __init__(self):
        self.nextValidator = 0

    def link(self, validator):
        if (self.nextValidator):
            self.nextValidator.link(validator)
        else:
            self.nextValidator = validator

    def callLinkedValidator(self, v1, v2, v3, v4):
        if (self.nextValidator):
            return self.nextValidator.validate(v1, v2, v3, v4)

        return False

    def validate(self, v1, v2, v3, v4):
        return False

class Validator1(BaseValidator):
    def validate(self, v1, v2, v3, v4):
        if (v1 and v2 and v3 and v4):
            return True
        return self.callLinkedValidator(v1, v2, v3, v4)

class Validator2(BaseValidator):
    def validate(self, v1, v2, v3, v4):
        if (v1 and v2 and v3 and not v4):
            return True
        return self.callLinkedValidator(v1, v2, v3, v4)

class Validator3(BaseValidator):
    def validate(self, v1, v2, v3, v4):
        if (v1 and not v2 and not v3 and not v4):
            return True
        return self.callLinkedValidator(v1, v2, v3, v4)

firstValidator = Validator1()
secondValidator = Validator2()
thirdValidator = Validator3()
firstValidator.link(secondValidator)
firstValidator.link(thirdValidator)
print(firstValidator.validate(False, False, True, False))

Sekali lagi, Anda mungkin menganggap ini berlebihan untuk contoh spesifik Anda, tetapi ini menciptakan kode yang jauh lebih bersih jika Anda berakhir dengan serangkaian kasus yang jauh lebih rumit yang perlu dipenuhi.

Jim Cullen
sumber
-2

Pendekatan sederhana adalah menemukan jawaban yang menurut Anda dapat diterima.

Ya = (boolean1 && boolean2 && boolean3 && boolean4) + + ...

Sekarang jika memungkinkan sederhanakan persamaan tersebut menggunakan aljabar boolean.

seperti dalam kasus ini, dapat diterima1 dan 2 digabungkan menjadi (boolean1 && boolean2 && boolean3).

Karenanya jawaban akhirnya adalah:

(boolean1 && boolean2 && boolean3) || 
((boolean1 && !boolean2 && !boolean3 && !boolean4)
Rupesh
sumber
-3

gunakan bidang bit :

unoin {
  struct {
    bool b1: 1;
    bool b2: 1;
    bool b3: 1;
    bool b4: 1;
  } b;
  int i;
} u;

// set:
u.b.b1=true;
...

// test
if (u.i == 0x0f) {...}
if (u.i == 0x0e) {...}
if (u.i == 0x08) {...}

PS :

Itu sangat disayangkan untuk CPPers '. Tapi UB bukan kekuatiran saya, cek di http://coliru.stacked-crooked.com/a/2b556abfc28574a1 .

hedzr
sumber
2
Hal ini menyebabkan UB karena mengakses union field yang tidak aktif.
HolyBlackCat
Secara formal UB dalam C ++, Anda tidak dapat menetapkan satu anggota serikat dan membaca dari yang lain. Secara teknis mungkin lebih baik untuk mengimplementasikan getter \ setter template untuk bit dengan nilai integral.
Swift - Pai Jumat
Saya pikir perilakunya akan bergeser ke Implementasi-Ditentukan jika seseorang mengubah alamat serikat menjadi unsigned char*, meskipun saya pikir hanya menggunakan sesuatu seperti ((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1mungkin akan lebih efisien.
supercat