Saya memiliki empat bool
nilai:
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 if
pernyataan 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?
c++
if-statement
Andrew Truckle
sumber
sumber
if
pernyataan kompleks . Selain itu, karena ini adalah flag boolean, Anda dapat membuat model setiap skenario sebagai konstanta dan memeriksanya.if (!((bValue1 && bValue2 && bValue3) || (bValue1 && !bValue2 && !bValue3 && !bValue4)))
bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
Jawaban:
Saya akan mengincar keterbacaan: Anda hanya memiliki 3 skenario, tangani dengan 3 skenario terpisah:
Mudah dibaca dan di-debug, IMHO. Selain itu, Anda dapat menetapkan variabel
whichScenario
saat melanjutkan denganif
.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
mungkintentu 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 :)
sumber
valid
dan memisahkannya dengan||
, daripada bermutasivalid
dalam 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 (untukif
) sehingga Anda tidak perlu menambahkan karakter apa pun ke ekspresi selain yang sudah ada.if($bValue1)
seperti yang selalu harus benar, secara teknis memungkinkan beberapa peningkatan kinerja kecil (meskipun kita berbicara tentang jumlah yang dapat diabaikan di sini).bValue4
Saya bertujuan untuk kesederhanaan dan keterbacaan.
Pastikan untuk mengganti nama skenario serta nama bendera dengan sesuatu yang deskriptif. Jika masuk akal untuk masalah spesifik Anda, Anda dapat mempertimbangkan alternatif ini:
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.
sumber
Kami dapat menggunakan peta Karnaugh dan mengurangi skenario Anda menjadi persamaan logis. Saya telah menggunakan pemecah peta Karnaugh Online dengan sirkuit untuk 4 variabel.
Ini menghasilkan:
Beralih
A, B, C, D
kebValue1, bValue2, bValue3, bValue4
, ini tidak lain adalah:Jadi
if
pernyataan Anda menjadi:true
.true
skenario menjadi persamaan logis, menambahkan komentar relevan yang menunjukkantrue
skenario adalah praktik yang baik.sumber
//!(ABC + AB'C'D') (By K-Map logic)
. Itu akan menjadi saat yang tepat bagi developer untuk mempelajari K-Maps jika dia belum mengetahuinya.E
danF
kondisi dan 4 skenario baru? Berapa lama waktu yang dibutuhkan untuk memperbaruiif
pernyataan ini dengan benar? Bagaimana tinjauan kode memeriksa apakah itu baik atau tidak? Masalahnya bukan pada sisi teknis tetapi pada sisi "bisnis".A
:ABC + AB'C'D' = A(BC + B'C'D')
(ini bahkan dapat difaktorkanA(B ^ C)'(C + D')
meskipun saya akan berhati-hati dengan menyebut 'penyederhanaan' ini).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:
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.
sumber
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 yangmySet.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.std::find
?).scenario
nilai menurut saya rawan kesalahan yang tidak perlu.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:
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.
sumber
Saya juga ingin mengajukan pendekatan lain.
Ide saya adalah mengubah bools menjadi integer dan kemudian membandingkannya menggunakan template variadic:
Perhatikan bagaimana sistem ini dapat mendukung hingga 32 bools sebagai input. mengganti
unsigned
denganunsigned long long
(atauuint64_t
) meningkatkan dukungan ke 64 kasus. Jika Anda tidak menyukainyaif (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u)
, Anda juga bisa menggunakan metode templat variadic lain:sumber
bitmap_from_bools
,, ataubools_to_bitmap
?bools_to_unsigned
. Bitmap adalah kata kunci yang bagus; diedit.summary!= 0b1111u &&...
.a != b || a != c
selalu benar jikab != c
Berikut versi yang disederhanakan:
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:
sumber
simple
memang 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.Pertimbangkan untuk menerjemahkan tabel Anda langsung ke dalam program Anda. Dorong program berdasarkan tabel, bukan meniru dengan logika.
sekarang
ini secara langsung mungkin mengkodekan tabel kebenaran Anda ke dalam kompiler.
Contoh langsung .
Anda juga bisa menggunakan
std::any_of
secara langsung: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.
sumber
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
boolean
metode "skenario" baru:Kemudian saya dapat menerapkannya secara rutin validasi saya seperti ini:
Dalam aplikasi langsung saya, 4 nilai bool sebenarnya diekstrak dari
DWORD
yang memiliki 4 nilai yang dikodekan ke dalamnya.Terima kasih sekali lagi semuanya.
sumber
INCLUDE_ITEM1
dll dengan cara yang lebih baik dan Anda semua baik-baik saja. :)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
/noexcept
opsional tetapi disarankan):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:
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.
sumber
Cara AC / C ++
Pendekatan ini dapat diskalakan seolah-olah jumlah kondisi yang valid bertambah, Anda dengan mudah menambahkan lebih banyak dari mereka ke daftar skenario.
sumber
true
. Kompiler yang menggunakan "apa pun yang bukan nol adalah benar" menyebabkan kode ini gagal. Perhatikan bahwatrue
harus dikonversi ke1
, hanya saja tidak perlu disimpan seperti itu.2 is not equal to true but evaluates to true
, kode saya tidak memaksaint 1 = true
dan 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?memcmp
untuk menguji kondisi boolean bukanlah cara C ++, dan saya agak meragukan bahwa itu juga cara C yang mapan.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 ):
Lebih jauh lagi, Anda dapat memperhatikan, bahwa boolean pertama harus selalu benar, yang merupakan kondisi entri, sehingga Anda bisa mendapatkan:
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):
Melakukannya dengan cara ini memiliki beberapa keuntungan dan kerugian:
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.
sumber
Seperti yang disarankan oleh mch, Anda dapat melakukan:
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.
sumber
Sedikit variasi dari jawaban bagus @ GianPaolo, yang mungkin lebih mudah dibaca oleh beberapa orang:
sumber
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 mengoptimalkanswitch()
pernyataan.Anda tentu saja dapat menggunakan konstanta dan OR bersama-sama dalam
case
pernyataan agar lebih mudah dibaca.sumber
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.
maka ekspresi Anda menjadi:
Memberi nama yang bermakna untuk variabel MAJORTRUE dan MAJORFALSE (serta sebenarnya ke bValue * vars) akan sangat membantu dengan keterbacaan dan pemeliharaan.
sumber
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).
sumber
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
NOT
mereka membutuhkan orang ketiga - 4 - untuk mengonfirmasi) yang paling mudah dibaca mungkin:dengan permintaan populer:
sumber
bValue
.Melakukan operasi bitwise terlihat sangat bersih dan mudah dipahami.
sumber
Saya menunjukkan a, b, c, d untuk kejelasan, dan A, B, C, D untuk pelengkap
Persamaan
Gunakan persamaan apa pun yang cocok untuk Anda.
sumber
sederhana
sumber
Hanya preferensi pribadi atas jawaban yang diterima, tetapi saya akan menulis:
sumber
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.
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 ++)
sumber
Sarang
if
mungkin lebih mudah dibaca bagi sebagian orang. Ini versi sayasumber
bValue1
blok, 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.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:
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.
Pada titik mana Anda memiliki konfigurasi awal Anda. sebagai sebuah array. Misalnya
std::array
memiliki operator persamaan:Pada titik mana sintaks Anda menjadi:
Seperti jawaban Gian Paolo yang singkat, jelas dan mudah diverifikasi / debuggable. Dalam kasus ini, kami telah mendelegasikan detail ekspresi boolean ke kompilator.
sumber
Anda tidak perlu khawatir tentang kombinasi flag boolean yang tidak valid jika Anda menyingkirkan flag boolean.
Anda jelas memiliki tiga keadaan (skenario). Akan lebih baik untuk memodelkannya dan mendapatkan properti boolean dari status tersebut, bukan sebaliknya.
Ini jelas lebih banyak kode daripada dalam jawaban Gian Paolo , tetapi tergantung pada situasi Anda, ini bisa jauh lebih bisa dipertahankan:
enum
kasus yang tidak tertangani dalamswitch
pernyataan akan menangkap pengambil properti yang tidak menangani skenario itu.Pendekatan ini juga memiliki keuntungan sampingan karena sangat efisien.
sumber
2 sen saya: menyatakan jumlah variabel (integer) sehingga
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.
sumber
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
BaseValidator
yang berisi referensi ke aBaseValidator
dan metode kevalidate
dan metode untuk memanggil validasi pada validator yang direferensikan.Kemudian Anda membuat sejumlah subclass yang diwarisi dari
BaseValidator
, menggantivalidate
metode dengan logika yang diperlukan untuk setiap validator.Kemudian menggunakannya sederhana, buat instance masing-masing validator Anda, dan setel masing-masing menjadi root yang lain:
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.
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.
sumber
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:
sumber
gunakan bidang bit :
PS :
Itu sangat disayangkan untuk CPPers '. Tapi UB bukan kekuatiran saya, cek di http://coliru.stacked-crooked.com/a/2b556abfc28574a1 .
sumber
unsigned char*
, meskipun saya pikir hanya menggunakan sesuatu seperti((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1
mungkin akan lebih efisien.