4 boole değerinin bazı durumlarda eşleşip eşleşmediğini kontrol etmek için mantık nasıl geliştirilir


118

Dört booldeğerim var:

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

Kabul edilebilir değerler şunlardır:

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

Yani, örneğin, bu senaryo kabul edilemez:

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

Şu anda ifkötü senaryoları tespit etmek için şu ifadeyi buldum :

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

Bu ifade mantığı geliştirilebilir / basitleştirilebilir mi?


8
Karmaşık ififade yerine bir tablo kullanırdım . Ek olarak, bunlar boole bayrakları olduğundan, her senaryoyu bir sabit olarak modelleyebilir ve buna karşı kontrol edebilirsiniz.
Zdeslav Vojkovic

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

14
senaryolar aslında neler? Bir şeylere sadece uygun isimler verirseniz, genellikle işler çok daha basitleşir, örneğinbool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
idclev 463035818

6
Anlamlı isimler kullanarak, her karmaşık koşulu bir yönteme çıkarabilir ve bu yöntemi if koşulunda çağırabilirsiniz. Çok daha okunaklı ve sürdürülebilir olacaktır. Örneğin, bağlantıda verilen örneğe bir göz atın. refactoring.guru/decompose-conditional
Hardik Modha

Yanıtlar:


195

Okunabilirliği hedeflerim: sadece 3 senaryonuz var, bunlarla 3 ayrı if ile ilgilenin:

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

Okuması ve hata ayıklaması kolay, IMHO. Ayrıca, whichScenariodevam ederken bir değişken atayabilirsiniz if.

Sadece 3 senaryo ile, "ilk 3 değer doğruysa, dördüncü değeri kontrol etmekten kaçınabilirim" gibi bir şey yapmam: kodunuzun okunması ve bakımı zorlaşır.

Şık bir çözüm değil olabilir elbette, ama bu durumda sorun yok: kolay ve okunabilir.

Mantığınız daha karmaşık hale gelirse, bu kodu atın ve mevcut farklı senaryoları saklamak için daha fazlasını kullanmayı düşünün (Zladeck'in önerdiği gibi).

Bu cevapta verilen ilk öneriyi gerçekten çok seviyorum : okunması kolay, hataya açık değil, bakımı yapılabilir

(Neredeyse) konu dışı:

StackOverflow'da burada çok fazla cevap yazmıyorum. Yukarıda kabul edilen cevabın tarihimde açık ara en çok takdir edilen cevap olması (düşündüğümden önce 5-10'dan fazla oy almamış olması) gerçekten komik olsa da, aslında bunu yapmanın "doğru" yolu olmadığını düşünüyorum.

Ancak basitlik genellikle "bunu yapmanın doğru yolu" dur, birçok insan bunu düşünüyor ve ben de benden daha fazla düşünmeliyim :)


1
sure @hessamhedieh, sadece az sayıda mevcut senaryo için sorun yok. dediğim gibi, işler daha karmaşık hale gelirse, başka bir şey arayın
Gian Paolo

4
Bu, ayrı ifade blokları içinde mutasyona uğratmak yerine, tüm koşulları başlatıcıya yığarak validve bunlarla ayırarak daha da basitleştirilebilir . Yoruma bir örnek veremem, ancak bunu çok açık hale getirmek için operatörleri sola dikey olarak hizalayabilirsiniz ; tek tek koşullar olması gerektiği kadar parantez içine alınmıştır, böylece ifadelere zaten orada olanın ötesinde herhangi bir karakter eklemeniz gerekmez. ||valid||if
Leushenko

1
@Leushenko, sanırım parantez ve && ve || koşullar hataya meyillidir (farklı bir yanıta sahip biri, OP'deki kodda parantez içinde bir hata olduğunu söyledi, belki de düzeltildi). Doğru hizalama yardımcı olabilir, elbette. Ama avantajı nedir? daha okunabilir mi? bakımı daha kolay mı? Ben öyle düşünmüyorum. Elbette sadece benim fikrim. Emin olun, kodda çok fazla ifs bulundurmaktan gerçekten nefret ediyorum.
Gian Paolo

3
Her if($bValue1)zaman doğru olması gerektiği gibi, teknik olarak bazı küçük performans iyileştirmelerine izin verdim (burada ihmal edilebilir miktarlardan bahsediyor olsak da).
Martijn

2
FWIW: sadece 2 senaryo var: ilk 2 senaryo aynı ve buna bağlı değilbValue4
Dancrumb

123

Basitliği ve okunabilirliği hedeflerim.

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

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

Senaryoların adlarını ve bayrakların adlarını açıklayıcı bir şeyle değiştirdiğinizden emin olun. Özel sorununuz için mantıklıysa, şu alternatifi düşünebilirsiniz:

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

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

Burada önemli olan yüklem mantığı değildir. Alanınızı tanımlıyor ve amacınızı açıkça ifade ediyor. Buradaki anahtar tüm girdilere ve ara değişkenlere iyi isimler vermektir. İyi değişken isimleri bulamıyorsanız, sorunu yanlış bir şekilde açıkladığınızın bir işareti olabilir.


3
+1 Ben de bunu yapardım. Tıpkı @ RedFilter'in işaret ettiği gibi ve kabul edilen cevabın aksine, bu kendi kendini belgelendirmektir. Senaryolara ayrı bir adımda kendi adlarını vermek çok daha okunaklı.
Andreas

106

Bir Karnaugh haritası kullanabilir ve senaryolarınızı mantıksal bir denkleme indirgeyebiliriz. Çevrimiçi Karnaugh harita çözücüsünü 4 değişken için devre ile kullandım .

görüntü açıklamasını buraya girin

Bu, şunları verir:

görüntü açıklamasını buraya girin

Değişen A, B, C, Diçin bValue1, bValue2, bValue3, bValue4, bu başka bir şey değildir:

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

Böylece ififadeniz şöyle olur:

if(!(bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4))
{
    // There is some error
}
  • Karnaugh Haritaları, değerlendirmeniz gereken birçok değişkeniniz ve birçok koşulunuz olduğunda özellikle yararlıdır true.
  • trueSenaryoları mantıksal bir denkleme indirdikten sonra, senaryoları gösteren ilgili yorumların eklenmesi trueiyi bir uygulamadır.

96
Teknik olarak doğru olmasına rağmen, bu kodun birkaç ay sonra başka bir geliştirici tarafından düzenlenebilmesi için çok sayıda yorum yapılması gerekir.
Zdeslav Vojkovic

22
@ZdeslavVojkovic: Denklemle sadece bir yorum eklerdim. //!(ABC + AB'C'D') (By K-Map logic). Bu, geliştiricinin K-Haritalarını henüz bilmiyorsa öğrenmesi için iyi bir zaman olurdu.
PW

11
Buna katılıyorum, ancak IMO'nun problemi, problemin etki alanıyla net bir şekilde eşleşmemesi, yani her koşulun belirli bir senaryoya nasıl eşlenerek değiştirilmesi / genişletilmesini zorlaştırmasıdır. Orada ne olur Eve F4 yeni senaryolar koşulları ve? Bu ififadeyi doğru şekilde güncellemek ne kadar sürer ? Kod incelemesi, doğru olup olmadığını nasıl kontrol eder? Sorun teknik tarafta değil, "iş" tarafında.
Zdeslav Vojkovic

7
Bence çarpanlarına ayırabilirsin A: ABC + AB'C'D' = A(BC + B'C'D')( A(B ^ C)'(C + D')bu 'basitleştirme' olarak adlandırırken dikkatli olsam bile bu faktör bile dikkate alınabilir).
Maciej Piechotka

28
@PW Bu yorum, kod kadar anlaşılabilir görünüyor ve bu nedenle biraz anlamsız. Daha iyi bir yorum, bu denklemi nasıl bulduğunuzu, yani ifadenin TTTT, TTTF ve TFFF'yi tetiklemesi gerektiğini açıklayabilir. Bu noktada, bunun yerine sadece bu üç koşulu koda yazabilir ve hiçbir açıklamaya ihtiyaç duymayabilirsiniz.
Bernhard Barker

58

Buradaki asıl soru şudur: Başka bir geliştiricinin (hatta yazarın) birkaç ay sonra bu kodu değiştirmesi gerektiğinde ne olur.

Bunu bit bayrakları olarak modellemeyi öneririm:

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");

Çok daha fazla senaryo veya daha fazla bayrak varsa, tablo yaklaşımı bayrakları kullanmaktan daha okunabilir ve genişletilebilirdir. Yeni bir senaryoyu desteklemek, tablodaki sadece başka bir satırı gerektirir.

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");
}

4
En bakımlı değil ama kesinlikle if koşulunu basitleştirir. Dolayısıyla, bitsel işlemler hakkında birkaç yorum bırakmak, burada imo'da mutlak bir gereklilik olacaktır.
Adam Zahran

6
IMO, tablo, ek senaryolar ve bayraklarla daha iyi ölçeklendiği için en iyi yaklaşımdır.
Zdeslav Vojkovic

Okuması kolay ve değişikliğe açık ilk çözümünüzü beğendim. 2 iyileştirme yapardım: 1: kullanılan boole değerlerinin açık bir göstergesiyle senaryoX'e değerler atayın, örneğin SCENARIO_2 = true << 3 | true << 2 | true << 1 | false;2: SCENARIO_X değişkenlerinden kaçının ve ardından tüm mevcut senaryoları a <std::set<int>. Bir senaryo eklemek, mySet.insert( true << 3 | false << 2 | true << 1 | false;belki sadece 3 senaryo için biraz abartılı bir şey olacak , OP cevabımda önerdiğim hızlı, kirli ve kolay çözümü kabul etti.
Gian Paolo

4
C ++ 14 veya üstünü kullanıyorsanız, bunun yerine ilk çözüm için ikili değişmez değerleri kullanmanızı öneririm - 0b1111, 0b1110 ve 0b1000 çok daha nettir. Standart kitaplığı ( std::find?) Kullanarak muhtemelen bunu biraz basitleştirebilirsiniz .
Bernhard Barker

2
Buradaki ikili değişmez değerlerin , ilk kodu temiz hale getirmek için asgari bir gereklilik olduğunu düşünüyorum. Mevcut haliyle tamamen şifreli. Açıklayıcı tanımlayıcılar yardımcı olabilir ama bundan emin değilim. Aslında, scenariodeğeri üretmek için yapılan bit işlemleri bana gereksiz yere hataya eğilimli geliyor.
Konrad Rudolph

27

Önceki cevabım zaten kabul edilmiş cevap, buraya hem okunabilir hem de kolay olduğunu düşündüğüm ve bu durumda gelecekteki değişikliklere açık olduğunu düşündüğüm bir şey ekliyorum:

@ZdeslavVojkovic cevabından başlayarak (ki bunu oldukça iyi buluyorum), şunu buldum:

#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;
}

İşte burada görün

Genelde amaçladığım "zarif ve bakımı kolay" (IMHO) çözüm budur, ancak gerçekten, OP vakası için, önceki "bir grup eğer" cevabım, zarif veya sürdürülemez olmasa bile OP gereksinimlerine daha iyi uyuyor.


Her zaman önceki yanıtınızı düzenleyebileceğinizi ve iyileştirmeler yapabileceğinizi biliyorsunuz .
Andreas

20

Ayrıca başka bir yaklaşım sunmak istiyorum.

Benim fikrim boolleri bir tam sayıya dönüştürmek ve ardından değişken şablonlar kullanarak karşılaştırmak:

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

Bu sistemin giriş olarak 32'ye kadar bool'u nasıl destekleyebileceğine dikkat edin. (veya ) unsignedile değiştirmek desteği 64 vakaya yükseltir. Eğer hoşunuza gitmiyorsa , başka bir değişken şablon yöntemini de kullanabilirsiniz:unsigned long longuint64_tif (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u)

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

2
Ana işlevin adı dışında bu yaklaşımı seviyorum: "bool ... neye ?" - Neden açıkça bitmap_from_bools, veya bools_to_bitmap?
Konrad Rudolph

evet @KonradRudolph, belki dışında daha iyi bir isim düşünemedim bools_to_unsigned. Bitmap iyi bir anahtar kelimedir; düzenlendi.
Stack Danny

Senin istediğini düşünüyorum summary!= 0b1111u &&.... a != b || a != ceğer her zaman doğrudurb != c
MooseBoys

17

İşte basitleştirilmiş bir versiyon:

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

Elbette, bu çözümün orijinal çözümden daha karmaşık olduğuna dikkat edin, anlamını anlamak daha zor olabilir.


Güncelleme: Yorumlardaki MSalters daha da basit bir ifade buldu:

if (bValue1&&(bValue2==bValue3)&&(bValue2>=bValue4)) ...

1
Evet, ama anlaşılması zor. Ama öneriniz için teşekkürler.
Andrew Truckle

Derleyicilerin ifadeyi basitleştirme becerisini referans olarak basitleştirmenizle karşılaştırdım: derleyici gezgini . gcc en uygun sürümü bulamadı ama çözümü hala iyi. Clang ve MSVC herhangi bir mantıksal ifade basitleştirmesi gerçekleştirmiyor gibi görünüyor.
Oliv

1
@AndrewTruckle: Daha okunabilir bir sürüme ihtiyacınız varsa, lütfen bunu söyleyin. "Basitleştirilmiş" dediniz, ancak orijinal versiyonunuzdan daha ayrıntılı bir versiyonu kabul ediyorsunuz.
geza

1
simplegerçekten belirsiz bir terimdir. Birçok kişi bu bağlamda, derleyicinin kod üretmesinin değil, geliştiricinin anlaması daha kolay olduğunu anlar, bu nedenle daha ayrıntılı bilgi gerçekten daha basit olabilir.
Zdeslav Vojkovic

1
@IsmaelMiguel: Bir mantık formülü terim sayısı için optimize edildiğinde, orijinal anlam genellikle kaybolur. Ama etrafına bir yorum koyabiliriz, bu yüzden ne yaptığı açıktır. Kabul edilen cevap için bile bir yorum zarar vermez.
geza

12

Tablolarınızı mümkün olduğunca doğrudan programınıza çevirmeyi düşünün. Programı mantıkla taklit etmek yerine, tablonun dışında sürün.

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

şimdi

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

bu doğrudan mümkün olduğu kadar doğruluk tablonuzu derleyiciye kodlar.

Canlı örnek .

std::any_ofDoğrudan şunları da kullanabilirsiniz :

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;
}) {
}

derleyici kodu satır içi yapabilir ve herhangi bir yinelemeyi ortadan kaldırabilir ve sizin için kendi mantığını oluşturabilir. Bu arada, kodunuz sorunu tam olarak nasıl anladığınızı yansıtır.


İlk versiyonun okunması çok kolay ve bakımı o kadar kolay ki, gerçekten beğendim. İkincisi okumak daha zor, en azından benim için ve belki ortalamanın üzerinde bir c ++ beceri seviyesi gerektiriyor, tabii ki benimki üzerinde. Herkesin yazabileceği bir şey değil. Yeni bir şey öğrendim, teşekkürler
Gian Paolo

11

Ben sadece burada birisinin çözümümü göstermesi için önerdiği yorumlarda olduğu gibi cevabımı veriyorum. Herkese içgörülerinden dolayı teşekkür etmek istiyorum.

Sonunda üç yeni "senaryo" booleanyöntemi eklemeyi seçtim :

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));
}

Sonra bunları doğrulama rutinimi şu şekilde uygulayabildim:

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

Canlı uygulamamda, 4 bool değeri aslında DWORDiçine kodlanmış 4 değeri olan a'dan çıkarılır .

Herkese tekrar teşekkürler.


1
Çözümü paylaştığınız için teşekkürler. :) Aslında karmaşık şartlardan daha iyi. Belki hala INCLUDE_ITEM1vb. Daha iyi bir ad verebilirsiniz ve hepiniz iyisinizdir. :)
Hardik Modha

1
@HardikModha Teknik olarak bunlar "Öğrenci öğeleri" ve bayrak "dahil edilip edilmeyeceklerini" belirtmek içindir. Dolayısıyla, kulağa genel görünse de adın bu bağlamda gerçekten anlamlı olduğunu düşünüyorum. :)
Andrew Truckle

11

Senaryoları adlandırmayı söyleyen herhangi bir cevap görmüyorum, ancak OP'nin çözümü tam olarak bunu yapıyor.

Bana göre en iyisi, her senaryonun ne olduğuna ilişkin açıklamayı bir değişken adı veya işlev adı içine almaktır. Bir yorumu görmezden gelme olasılığınız bir isimden daha fazladır ve gelecekte mantığınız değişirse, bir yorumdan çok bir adı değiştirme olasılığınız daha yüksektir. Bir yorumu yeniden düzenleyemezsiniz.

Bu senaryoları işlevinizin dışında yeniden kullanmayı planlıyorsanız (veya isteyebilirsiniz), değerlendirdiğini söyleyen ( constexpr/ noexceptisteğe bağlı ancak önerilen) bir işlev yapın :

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

Mümkünse bu sınıf yöntemlerini yapın (OP'nin çözümünde olduğu gibi). Mantığı yeniden kullanacağınızı düşünmüyorsanız, işlevinizin içindeki değişkenleri kullanabilirsiniz:

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;

Derleyici büyük ihtimalle bValue1 yanlışsa tüm senaryoların yanlış olduğunu anlayacaktır. Hızlı yapmak için endişelenmeyin, sadece doğru ve okunaklı. Kodunuzun profilini çıkarırsanız ve bunu bir darboğaz olarak bulursanız, çünkü derleyici -O2 veya daha yüksek bir değerde alt-optimal kod oluşturduysa, yeniden yazmaya çalışın.


Bunu Gian Paolo'nun (zaten güzel) çözümünden biraz daha fazla seviyorum: Kontrol akışını ve üzerine yazılan bir değişkenin - daha işlevsel tarzın - kullanımından kaçınıyor.
Dirk Herrmann

9

AC / C ++ yolu

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

Bu yaklaşım, geçerli koşulların sayısı arttıkça ölçeklenebilir, kolayca senaryo listesine daha fazlasını ekleyebilirsiniz.


Yine de bunun yanlış olduğundan oldukça eminim. Derleyicinin yalnızca tek bir ikili gösterimi kullandığını varsayar true. "Sıfır olmayan her şey doğrudur" kullanan bir derleyici bu kodun başarısız olmasına neden olur. Not truegerekir dönüştürmek için 1, sadece olması gerekmez saklanır gibi.
MSalters

@MSalters, tnx, demek istediğini almak ve ben tür gibi bunun farkındayım 2 is not equal to true but evaluates to truebenim kod doesnt kuvveti, int 1 = trueve boyu gerçek en aynı int değerine dönüştürülür kadar eserler, SO burada sorum şu: derleyici dönüştürerek rastgele hareket etmelidir Neden underlying int için doğru, lütfen daha fazla ayrıntı verebilir misiniz?
hessam hedieh

Bir Sahne memcmpboolean koşulları test etmektir değil doğrusu şüphesiz ya, oluşmuş bir C yolu olduğunu C ++ yolu, vb.
Konrad Rudolph

@hessamhedieh: Mantığınızdaki sorun, "doğruyu temeldeki int'e dönüştürmek". Yani değil , derleyiciler nasıl çalıştığını
MSalters

Kodunuz karmaşıklığı O (1) 'den O (n)' ye yükseltir. Herhangi bir dilde gitmenin bir yolu değil - C / C ++ 'ı bir kenara bırakın.
mabel

9

İlk iki senaryonun benzer olduğunu fark etmek kolaydır - koşulların çoğunu paylaşırlar. Şu anda hangi senaryoda olduğunuzu seçmek istiyorsanız, bunu şöyle yazabilirsiniz (bu, değiştirilmiş bir @ gian-paolo'nun çözümü):

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

Daha ileri gidersek, ilk mantıksal değerin her zaman doğru olması gerektiğini fark edebilirsiniz, bu da bir giriş koşulu, böylece şunu elde edebilirsiniz:

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
}

Dahası, bValue2 ve bValue3'ün bir şekilde bağlantılı olduğunu şimdi açıkça görebilirsiniz - durumlarını bazı harici işlevlere veya daha uygun bir ada sahip değişkenlere çıkartabilirsiniz (bu her zaman kolay veya uygun değildir):

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
}

Bu şekilde yapmanın bazı avantajları ve dezavantajları vardır:

  • koşullar daha küçüktür, bu nedenle onlar hakkında akıl yürütmek daha kolaydır,
  • Bu koşulları daha anlaşılır kılmak için güzel bir yeniden adlandırma yapmak daha kolaydır,
  • ancak kapsamı anlamaları gerekir,
  • dahası daha katı

Yukarıdaki mantıkta değişiklikler olacağını tahmin ediyorsanız, @ gian-paolo tarafından sunulan daha basit bir yaklaşım kullanmalısınız .

Aksi takdirde, bu koşullar iyi oluşturulmuşsa ve asla değişmeyecek bir tür "katı kurallar" ise, son kod parçacığımı düşünün.


7

Mch tarafından önerildiği gibi şunları yapabilirsiniz:

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

burada ilk satır ilk iki iyi durumu kapsar ve ikinci satır sonuncuyu kapsar.

Live Demo, etrafta oynadığım ve davalarınızın geçtiği yer.


7

@ GianPaolo'nun güzel cevabında, bazılarının okumayı daha kolay bulabileceği küçük bir değişiklik:

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

7

Her cevap aşırı derecede karmaşık ve okunması zor. Buna en iyi çözüm bir switch()ifadedir. Hem okunabilir hem de ek durum eklemeyi / değiştirmeyi basitleştirir. Derleyiciler de switch()ifadeleri optimize etmede iyidir .

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

Daha casefazla okunabilirlik için elbette sabitleri ve VEYA bunları ifadelerde birlikte kullanabilirsiniz .


Eski bir C-programcısı olarak, bir "PackBools" makrosu tanımlar ve bunu hem "anahtar (PackBools (a, b, c, d))" hem de durumlar için, örneğin doğrudan "PackBools durumu (true , true ...) "veya bunları yerel sabitler olarak tanımlayın." const unsigned int scenario1 = PackBools (true, true ...); "
Simon F

6

Netlik için kısayol değişkenlerini de kullanırdım. Daha önce belirtildiği gibi senaryo 1, senaryo 2'ye eşittir, çünkü bValue4'ün değeri bu iki senaryonun doğruluğunu etkilemez.

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

sonra ifade beomes:

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

MAJORTRUE ve MAJORFALSE değişkenlerine (ve aslında bValue * değişkenlerine) anlamlı isimler vermek okunabilirlik ve bakım konusunda çok yardımcı olacaktır.


6

Belirli "eğer" ifadesine değil, sorunun okunabilirliğine odaklanın.

Bu daha fazla kod satırı üretecek olsa da, bazıları bunun aşırı veya gereksiz olduğunu düşünebilir. Senaryolarınızı belirli booleanlardan soyutlamanın okunabilirliği korumanın en iyi yolu olduğunu öneririm.

Her şeyi anlaşılır adlarla sınıflara ayırarak (sadece işlevleri kullanmaktan çekinmeyin veya başka bir aracı tercih edin) - her senaryonun arkasındaki anlamları çok daha kolay gösterebiliriz. Daha da önemlisi, çok sayıda hareketli parçaya sahip bir sistemde - mevcut sistemlerinize bakım yapmak ve bunlara katılmak daha kolaydır (yine, ne kadar fazla kod dahil olsa da).

#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;
}

5
Bir noktada, ayrıntı okunabilirliğe zarar vermeye başlar. Sanırım bu çok ileri gidiyor.
JollyJoker

2
@JollyJoker Aslında bu özel durumda aynı fikirdeyim - ancak, OP'nin her şeyi son derece genel bir şekilde adlandırmasından kaynaklanan içgüdüsel hislerim, onların "gerçek" kodlarının muhtemelen verdikleri örnekten çok daha karmaşık olmasıdır. Gerçekten, bu alternatifi oraya koymak istedim, çünkü onu çok daha karmaşık / ilgili bir şey için nasıl yapılandıracağım. Ama haklısın - OP'lerin özel örneği için, aşırı derecede ayrıntılı ve işleri daha da kötüleştiriyor.

5

Neyi temsil ettiklerine bağlı.

Örneğin, 1 bir anahtar ise ve 2 ve 3 aynı fikirde olması gereken iki kişiyse ( onaylamak NOTiçin üçüncü bir kişiye ihtiyaç duydukları konusunda hemfikir olmadıkları sürece - 4 -) en okunaklı olanlar şunlar olabilir:

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

popüler istek üzerine:

Key &&
    (
        (Alice && Bob)   
        || 
        ((!Alice && !Bob) && !Charlie)
    )

2
Haklı olabilirsin, ancak soruyu göstermek için sayıları kullanmak cevabını azaltıyor. Açıklayıcı isimler kullanmayı deneyin.
jxh

1
@jxh Bunlar OP'nin kullandığı sayılardır. Şimdi kaldırdım bValue.
ispiro

@jxh Umarım şimdi daha iyidir.
ispiro

4

Bitsel işlem yapmak çok temiz ve anlaşılır görünüyor.

int bitwise = (bValue4 << 3) | (bValue3 << 2) | (bValue2 << 1) | (bValue1);
if (bitwise == 0b1111 || bitwise == 0b0111 || bitwise == 0b0001)
{
    //satisfying condition
}

1
Bitsel karşılaştırma bana okunabilir görünüyor. Kompozisyon ise yapay görünüyor.
xtofl

3

Açıklık için a, b, c, d'yi ve tamamlayıcılar için A, B, C, D'yi ifade ediyorum

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

Denklem

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

Size uyan denklemleri kullanın.


3
If (!bValue1 || (bValue2 != bValue3) || (!bValue4 && bValue2))
{
// you have a problem
}
  • b1 her zaman doğru olmalı
  • b2 her zaman b3'e eşit olmalıdır
  • ve b2 (ve b3) doğruysa b4 yanlış olamaz

basit


3

Kabul edilen cevap yerine kişisel bir tercih, ama şunu yazardım:

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);

2

İlk olarak, yalnızca senaryo kontrolünü değiştirebileceğinizi varsayarsak, okunabilirliğe odaklanırım ve kontrolü sadece arayabilmeniz için bir fonksiyona sararım if(ScenarioA()).


Şimdi, bunu gerçekten optimize etmek istediğiniz / buna ihtiyaç duyduğunuzu varsayarsak, sıkı bağlanmış Boole'ları sabit tam sayılara dönüştürmenizi ve bunlarda bit operatörlerini kullanmanızı tavsiye ederim.

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
}

Bu, senaryoları ifade etmeyi, bunun bir parçası olanları listelemek kadar kolay hale getirir, doğru koşula atlamak için bir switch deyimi kullanmanıza ve bunu daha önce görmemiş olan geliştiricilerin kafasını karıştırmanıza olanak tanır. (C # RegexOptions bayrakları ayarlamak için bu kalıbı kullanır, bir c ++ kitaplık örneği olup olmadığını bilmiyorum)


Aslında dört bool değeri kullanmıyorum, dört gömülü BOOLS içeren bir DWORD kullanıyorum. Şimdi değiştirmek için çok geç. Ama öneriniz için teşekkürler.
Andrew Truckle

2

İç içe geçmiş ife- postaları bazı insanlar için okumak daha kolay olabilir. İşte benim versiyonum

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

Şahsen, eğer mümkünse ifadeler varsa iç içe geçmekten kaçınırım. Bu vaka güzel ve okunaklı olsa da, yeni olasılıklar eklendiğinde, yuvalama çok zor okunabilir. Ancak senaryolar asla değişmezse, kesinlikle güzel ve okunabilir bir çözümdür.
Dnomyar96

@ Dnomyar96 katılıyorum. Şahsen ben de iç içe geçmiş iflerden kaçınırım. Bazen mantık karmaşıksa, mantığı parçalara ayırarak anlamak benim için daha kolay oluyor. Örneğin, bValue1bloğa girdikten sonra, içindeki her şeyi zihinsel sürecinizde yeni ve yeni bir sayfa olarak ele alabilirsiniz. Bahse girerim, soruna yaklaşım şekli çok kişisel ve hatta kültürel bir şey olabilir.
sardok

1

Bu soruya birkaç doğru cevap verildi, ancak farklı bir görüşe sahip olacağım: eğer kod çok karmaşık görünüyorsa, bir şeyler doğru değildir . Kodun hata ayıklaması zor olacak ve büyük olasılıkla "yalnızca tek kullanımlık" olacaktır.

Gerçek hayatta böyle bir durum bulduğumuzda:

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

Dört durum böylesine kesin bir modelle birbirine bağlandığında, modelimizdeki bazı "varlıkların" konfigürasyonuyla uğraşıyoruz .

Uç bir metafor, belirli bir özgürlük derecesine bağlı bileşenlere sahip üniter varlıklar olarak varlıklarının farkında olmasaydık, bir modelde bir "insanları" nasıl tanımlayacağımızdır: "gövde" nin bağımsız durumlarını tanımlamamız gerekirdi. "kollar", "bacaklar" ve "baş", tanımlanan sistemi anlamlandırmayı karmaşık hale getirebilir. Anında bir sonuç, doğal olmayan bir şekilde karmaşık olan mantıksal ifadeler olacaktır.

Açıkçası, karmaşıklığı azaltmanın yolu soyutlamadır ve c ++ 'da tercih edilen bir araç nesne paradigmasıdır .

Öyleyse soru şu: neden böyle bir model var? Bu nedir ve neyi temsil ediyor?

Cevabı bilmediğimiz için, matematiksel bir soyutlamaya geri dönebiliriz: dizi : Her biri artık bir dizi olan üç senaryomuz var.

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

Hangi noktada ilk yapılandırmanıza sahip olursunuz. bir dizi olarak. Örneğin std::arraybir eşitlik operatörü vardır:

Söz diziminiz hangi noktada olur:

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

}

Tıpkı Gian Paolo'nun cevabı gibi, kısa, açık ve kolayca doğrulanabilir / hata ayıklanabilir. Bu durumda, boole ifadelerinin ayrıntılarını derleyiciye verdik.


1

Boole bayraklarından kurtulursanız geçersiz boole bayrakları kombinasyonları hakkında endişelenmenize gerek kalmaz.

Kabul edilebilir değerler şunlardır:

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

Açıkça üç durumunuz var (senaryolar). Bunu modellemek ve boole özelliklerini bu durumlardan türetmek daha iyi olur , tersi değil.

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

Bu kesinlikle Gian Paolo'nun cevabındakinden daha fazla kod. , ancak durumunuza bağlı olarak bu çok daha bakımı yapılabilir olabilir:

  • Ek boole özellikleri veya senaryoları eklenirse, değiştirilecek merkezi bir işlev kümesi vardır.
    • Özellikler eklemek yalnızca tek bir işlev eklemeyi gerektirir.
    • Bir senaryo ekliyorsanız enum, switchifadelerdeki işlenmemiş durumlar hakkında derleyici uyarılarını etkinleştirmek, bu senaryoyu işlemeyen özellik alıcıları yakalayacaktır.
  • Boole özelliklerini dinamik olarak değiştirmeniz gerekirse, kombinasyonlarını her yerde yeniden doğrulamanıza gerek yoktur. Tek tek boole bayraklarını değiştirmek yerine (geçersiz bayrak kombinasyonlarına neden olabilir), bunun yerine bir senaryodan diğerine geçiş yapan bir durum makinesine sahip olursunuz.

Bu yaklaşım aynı zamanda çok verimli olma yan faydasına da sahiptir.


0

Benim 2 sentim: bir değişken toplamı (tamsayı) beyan et, böylece

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

Toplamı istediğiniz koşullara göre kontrol edin, hepsi bu. Bu şekilde ileride kolayca daha fazla koşul ekleyebilir ve okumayı oldukça kolay hale getirebilirsiniz.


0

Kabul edilen yanıt, yalnızca 3 vakanız olduğunda ve her birinin mantığının basit olduğu durumlarda iyidir.

Ancak her bir durumun mantığı daha karmaşıksa veya daha birçok durum varsa, çok daha iyi bir seçenek, sorumluluk zinciri tasarım modelini kullanmaktır.

Bir oluşturmak BaseValidatorbir başvuru içeriyor hangi BaseValidatorve bir yöntem validateve başvurulan doğrulayıcı üzerinde doğrulama çağırmak için bir yöntem.

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

Ardından BaseValidator, validateher bir doğrulayıcı için gerekli olan mantığı kullanarak yöntemi geçersiz kılarak, öğesinden miras alan bir dizi alt sınıf yaratırsınız.

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);
    }
}

Daha sonra bunu kullanmak basittir, her bir doğrulayıcıyı somutlaştırın ve her birini diğerlerinin kökü olacak şekilde ayarlayın:

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

Temelde, her bir doğrulama vakasının, (a) doğrulamanın bu durumla eşleşip eşleşmediğini belirlemekten ve (b) yoksa doğrulamayı zincirdeki başka birine göndermekten sorumlu olan kendi sınıfı vardır .

Lütfen C ++ ile aşina olmadığımı unutmayın. Sözdizimini çevrimiçi bulduğum bazı örneklerden eşleştirmeye çalıştım, ancak bu işe yaramazsa, daha çok sözde kod gibi davranın. Ayrıca, tercih edilirse temel olarak kullanılabilecek eksiksiz bir çalışan Python örneğim de aşağıda var.

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))

Yine, bu aşırılığı kendi örneğiniz için bulabilirsiniz, ancak karşılanması gereken çok daha karmaşık bir dizi durumla karşılaşırsanız çok daha temiz bir kod oluşturur.


-2

Kabul edilebilir olduğunu düşündüğünüz cevabı bulmak basit bir yaklaşımdır.

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

Şimdi eğer mümkünse, boole cebri kullanarak denklemi basitleştirin.

bu durumda olduğu gibi, kabul edilebilir1 ve 2, (boolean1 && boolean2 && boolean3) .

Dolayısıyla son cevap:

(boolean1 && boolean2 && boolean3) || 
((boolean1 && !boolean2 && !boolean3 && !boolean4)

-3

kullanmak bit alanını :

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 :

Bu, CPPers 'için çok yazık. Ancak UB benim endişem değil, http://coliru.stacked-crooked.com/a/2b556abfc28574a1 adresinden kontrol edin .


2
Bu, etkin olmayan bir birleşim alanına erişim nedeniyle UB'ye neden olur.
HolyBlackCat

Resmi olarak C ++ 'da UB'dir, bir sendika üyesini ayarlayıp diğerinden okuyamazsınız. Teknik olarak, integral değer bitleri için şablonlu alıcılar \ ayarlayıcılar uygulamak daha iyi olabilir.
Swift - Friday Pie

Biri sendikanın adresini bir adrese çevirecek olsaydı davranış Uygulama-Tanımlı'ya kayacağını unsigned char*düşünüyorum, ancak basitçe benzer bir şeyi kullanmanın ((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1muhtemelen daha verimli olacağını düşünüyorum .
supercat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.