Cách cải thiện logic để kiểm tra xem 4 giá trị boolean có khớp với một số trường hợp hay không


118

Tôi có bốn boolgiá trị:

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

Các giá trị được chấp nhận là:

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

Vì vậy, ví dụ, trường hợp này không được chấp nhận:

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

Hiện tại, tôi đã đưa ra iftuyên bố này để phát hiện các tình huống xấu:

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

Logic câu lệnh đó có thể được cải thiện / đơn giản hóa không?


8
Tôi sẽ sử dụng một bảng thay vì ifcâu lệnh phức tạp . Ngoài ra, vì đây là các cờ boolean, bạn có thể lập mô hình từng trường hợp như một hằng số và kiểm tra nó.
Zdeslav Vojkovic

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

14
các kịch bản thực sự là gì? Thường thì mọi thứ trở nên đơn giản hơn nhiều nếu bạn chỉ cần cung cấp tên những thứ thích hợp, ví dụbool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
idclev 463035818

6
Sử dụng các tên có ý nghĩa, bạn có thể trích xuất từng điều kiện phức tạp thành một phương thức và gọi phương thức đó trong điều kiện if. Nó sẽ dễ đọc và dễ bảo trì hơn nhiều. ví dụ: Hãy xem ví dụ được cung cấp trong liên kết. refactoring.guru/decompose-conditional
Hardik Modha

Câu trả lời:


195

Tôi muốn hướng tới tính dễ đọc: bạn chỉ có 3 kịch bản, hãy xử lý chúng bằng 3 ifs riêng biệt:

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

Dễ đọc và gỡ lỗi, IMHO. Ngoài ra, bạn có thể gán một biến whichScenariotrong khi tiếp tục với if.

Chỉ với 3 trường hợp, tôi sẽ không đi với một cái gì đó như "nếu 3 giá trị đầu tiên là đúng, tôi có thể tránh kiểm tra giá trị thứ tư": nó sẽ làm cho mã của bạn khó đọc và duy trì hơn.

Không phải là một giải pháp thanh lịch có lẽ chắc chắn, nhưng trong trường hợp này là ok: dễ dàng và dễ đọc.

Nếu logic của bạn trở nên phức tạp hơn, hãy vứt bỏ đoạn mã đó đi và cân nhắc sử dụng thêm thứ gì đó để lưu trữ các tình huống có sẵn khác nhau (như Zladeck đang đề xuất).

Tôi thực sự thích gợi ý đầu tiên được đưa ra trong câu trả lời này : dễ đọc, không dễ bị lỗi, có thể bảo trì

(Gần như) lạc đề:

Tôi không viết nhiều câu trả lời ở đây tại StackOverflow. Thật sự rất buồn cười khi câu trả lời được chấp nhận ở trên cho đến nay là câu trả lời được đánh giá cao nhất trong lịch sử của tôi (tôi chưa bao giờ có hơn 5-10 phiếu ủng hộ trước khi tôi nghĩ) trong khi thực sự không phải là cách tôi thường nghĩ là "đúng" để làm điều đó.

Nhưng đơn giản thường là "cách làm đúng", nhiều người có vẻ nghĩ điều này và tôi nên nghĩ nó nhiều hơn tôi :)


1
chắc chắn @hessamhedieh, nó chỉ phù hợp với một số ít kịch bản có sẵn. như tôi đã nói, nếu nhận được những điều phức tạp hơn, cái nhìn tốt hơn cho cái gì khác
Gian Paolo

4
Điều này có thể được đơn giản hóa hơn nữa bằng cách xếp chồng tất cả các điều kiện vào trình khởi tạo validvà tách chúng bằng ||, thay vì thay đổi validtrong các khối câu lệnh riêng biệt. Tôi không thể đặt ví dụ trong nhận xét nhưng bạn có thể căn chỉnh theo chiều dọc các ||toán tử dọc bên trái để làm cho điều này rất rõ ràng; các điều kiện riêng lẻ đã được đặt trong ngoặc đơn nhiều khi chúng cần (cho if), vì vậy bạn không cần thêm bất kỳ ký tự nào vào biểu thức ngoài những gì đã có.
Leushenko

1
@Leushenko, tôi nghĩ rằng việc trộn dấu ngoặc đơn, && và || điều kiện khá dễ xảy ra lỗi (ai đó trong một câu trả lời khác nói rằng có lỗi trong dấu ngoặc đơn trong mã trong OP, có thể nó đã được sửa). Căn chỉnh thích hợp có thể hữu ích, chắc chắn. Nhưng lợi thế là gì? dễ đọc hơn? dễ bảo trì hơn? Tôi không nghĩ vậy. Hiển nhiên đó chỉ là ý kiến ​​của tôi thôi. An chắc, tôi thực sự ghét có nhiều ifs trong mã.
Gian Paolo

3
Tôi đã gói nó trong một if($bValue1)điều mà điều đó luôn phải đúng, về mặt kỹ thuật cho phép một số cải thiện hiệu suất nhỏ (mặc dù chúng ta đang nói về số tiền không đáng có ở đây).
Martijn

2
FWIW: chỉ có 2 kịch bản: 2 đầu tiên là kịch bản giống nhau và không phụ thuộc vàobValue4
Dancrumb

123

Tôi muốn hướng tới sự đơn giản và dễ đọc.

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

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

Đảm bảo thay thế tên của các tình huống cũng như tên của các cờ bằng một cái gì đó mang tính mô tả. Nếu nó phù hợp với vấn đề cụ thể của bạn, bạn có thể xem xét phương án thay thế này:

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

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

Điều quan trọng ở đây không phải là logic vị từ. Nó mô tả miền của bạn và thể hiện rõ ràng ý định của bạn. Chìa khóa ở đây là đặt tên tốt cho tất cả các đầu vào và biến trung gian. Nếu bạn không thể tìm thấy tên biến tốt, đó có thể là dấu hiệu cho thấy bạn đang mô tả vấn đề một cách sai lầm.


3
+1 Đây là những gì tôi cũng sẽ làm. Giống như @RedFilter đã chỉ ra, và trái ngược với câu trả lời được chấp nhận, đây là tài liệu tự ghi lại. Đặt tên riêng cho các kịch bản trong một bước riêng biệt sẽ dễ đọc hơn nhiều.
Andreas

106

Chúng tôi có thể sử dụng bản đồ Karnaugh và giảm các tình huống của bạn thành một phương trình logic. Tôi đã sử dụng trình giải bản đồ Karnaugh Trực tuyến với mạch cho 4 biến.

nhập mô tả hình ảnh ở đây

Điều này mang lại:

nhập mô tả hình ảnh ở đây

Thay đổi A, B, C, Dthành bValue1, bValue2, bValue3, bValue4, điều này không có gì khác ngoài:

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

Vì vậy, iftuyên bố của bạn trở thành:

if(!(bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4))
{
    // There is some error
}
  • Bản đồ Karnaugh đặc biệt hữu ích khi bạn có nhiều biến số và nhiều điều kiện cần đánh giá true.
  • Sau khi giảm các truetình huống thành một phương trình logic, thêm các nhận xét có liên quan cho biết các truetình huống là một phương pháp hay.

96
Mặc dù đúng về mặt kỹ thuật, mã này cần rất nhiều nhận xét để được một nhà phát triển khác chỉnh sửa vài tháng sau đó.
Zdeslav Vojkovic

22
@ZdeslavVojkovic: Tôi chỉ cần thêm một nhận xét với phương trình. //!(ABC + AB'C'D') (By K-Map logic). Đó sẽ là thời điểm tốt để nhà phát triển tìm hiểu K-Maps nếu anh ta chưa biết chúng.
PW

11
Tôi đồng ý với điều đó, nhưng IMO có vấn đề là nó không ánh xạ rõ ràng đến miền vấn đề, tức là cách mỗi điều kiện ánh xạ đến kịch bản cụ thể khiến khó thay đổi / mở rộng. Điều gì xảy ra khi có EFđiều kiện và 4 kịch bản mới? Mất bao lâu để cập nhật ifcâu lệnh này một cách chính xác? Làm thế nào để xem lại mã kiểm tra xem nó có ổn hay không? Vấn đề không phải ở phía kỹ thuật mà ở phía "doanh nghiệp".
Zdeslav Vojkovic

7
Tôi nghĩ bạn có thể suy ra A: ABC + AB'C'D' = A(BC + B'C'D')(điều này thậm chí có thể được tính đến A(B ^ C)'(C + D')mặc dù tôi sẽ cẩn thận khi gọi điều này là 'đơn giản hóa').
Maciej Piechotka

28
@PW Nhận xét đó có vẻ dễ hiểu như mã, và do đó hơi vô nghĩa. Một nhận xét tốt hơn sẽ giải thích cách bạn thực sự nghĩ ra phương trình đó, tức là câu lệnh sẽ kích hoạt TTTT, TTTF và TFFF. Tại thời điểm đó, bạn cũng có thể chỉ cần viết ba điều kiện đó trong mã thay thế và không cần giải thích gì cả.
Bernhard Barker

58

Câu hỏi thực sự ở đây là: điều gì sẽ xảy ra khi một nhà phát triển khác (hoặc thậm chí là tác giả) phải thay đổi mã này vài tháng sau đó.

Tôi sẽ đề xuất mô hình hóa điều này dưới dạng cờ 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");

Nếu có nhiều kịch bản hơn hoặc nhiều cờ hơn, thì cách tiếp cận bảng sẽ dễ đọc và dễ mở rộng hơn là sử dụng cờ. Hỗ trợ một kịch bản mới chỉ cần một hàng khác trong bảng.

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
Không phải là dễ bảo trì nhất nhưng chắc chắn đơn giản hóa điều kiện if. Vì vậy, để lại một vài nhận xét xung quanh các hoạt động bitwise sẽ là một điều hoàn toàn cần thiết ở đây imo.
Adam Zahran

6
IMO, bảng là cách tiếp cận tốt nhất vì nó mở rộng quy mô tốt hơn với các kịch bản và cờ bổ sung.
Zdeslav Vojkovic

Tôi thích giải pháp đầu tiên của bạn, dễ đọc và dễ sửa đổi. Tôi sẽ thực hiện 2 cải tiến: 1: gán giá trị cho scriptX với chỉ báo rõ ràng về các giá trị boolean được sử dụng, ví dụ SCENARIO_2 = true << 3 | true << 2 | true << 1 | false;2: tránh các biến SCENARIO_X và sau đó lưu trữ tất cả các kịch bản có sẵn trong a <std::set<int>. Thêm một kịch bản sẽ chỉ là một cái gì đó mySet.insert( true << 3 | false << 2 | true << 1 | false;có thể hơi quá mức cần thiết cho chỉ 3 kịch bản, OP đã chấp nhận giải pháp nhanh chóng, bẩn thỉu và dễ dàng mà tôi đã đề xuất trong câu trả lời của mình.
Gian Paolo

4
Nếu bạn đang sử dụng C ++ 14 trở lên, thay vào đó, tôi khuyên bạn nên sử dụng các ký tự nhị phân cho giải pháp đầu tiên - 0b1111, 0b1110 và 0b1000 rõ ràng hơn nhiều. Bạn cũng có thể đơn giản hóa điều này một chút bằng cách sử dụng thư viện chuẩn ( std::find?).
Bernhard Barker

2
Tôi thấy rằng các ký tự nhị phân ở đây sẽ là một yêu cầu tối thiểu để làm cho mã đầu tiên sạch sẽ. Ở dạng hiện tại, nó hoàn toàn khó hiểu. Số nhận dạng mô tả có thể hữu ích nhưng tôi thậm chí không chắc về điều đó. Trên thực tế, các thao tác bit để tạo ra scenariogiá trị khiến tôi dễ bị lỗi một cách không cần thiết.
Konrad Rudolph

27

Câu trả lời trước đây của tôi đã là câu trả lời được chấp nhận, tôi thêm điều gì đó ở đây mà tôi nghĩ là vừa dễ đọc, vừa dễ dàng và trong trường hợp này có thể mở ra các sửa đổi trong tương lai:

Bắt đầu với câu trả lời @ZdeslavVojkovic (mà tôi thấy khá hay), tôi đã nghĩ ra điều này:

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

Nhìn thấy nó tại nơi làm việc ở đây

Chà, đó là giải pháp "tao nhã và có thể bảo trì" (IMHO) mà tôi thường hướng tới, nhưng thực sự, đối với trường hợp OP, câu trả lời "loạt ifs" trước đây của tôi phù hợp hơn với các yêu cầu OP, ngay cả khi nó không thanh lịch và cũng không thể bảo trì.


Bạn biết rằng bạn luôn có thể chỉnh sửa câu trả lời trước đó của mình và thực hiện các cải tiến.
Andreas

20

Tôi cũng muốn gửi một cách tiếp cận khác.

Ý tưởng của tôi là chuyển đổi các bools thành một số nguyên và sau đó so sánh bằng cách sử dụng các mẫu khác nhau:

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

Lưu ý cách hệ thống này có thể hỗ trợ tối đa 32 bools làm đầu vào. thay thế unsignedbằng unsigned long long(hoặc uint64_t) tăng hỗ trợ lên 64 trường hợp. Nếu bạn không thích if (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u), bạn cũng có thể sử dụng thêm một phương pháp mẫu khác nhau:

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
Tôi thích cách tiếp cận này, ngoại trừ tên của hàm chính: “từ bool… đến cái gì ?” - Tại sao không rõ ràng bitmap_from_bools, hoặc bools_to_bitmap?
Konrad Rudolph

vâng @KonradRudolph, tôi không thể nghĩ ra một cái tên nào hay hơn, ngoại trừ có thể bools_to_unsigned. Bitmap là một từ khóa tốt; đã chỉnh sửa.
Stack Danny

Tôi nghĩ bạn muốn summary!= 0b1111u &&.... a != b || a != cluôn luôn là đúng nếub != c
MooseBoys

17

Đây là một phiên bản đơn giản hóa:

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

Lưu ý, tất nhiên, giải pháp này khó hiểu hơn so với giải pháp ban đầu, ý nghĩa của nó có thể khó hiểu hơn.


Cập nhật: MSalters trong các nhận xét đã tìm thấy một biểu thức thậm chí còn đơn giản hơn:

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

1
Có, nhưng khó hiểu. Nhưng cảm ơn đã gợi ý.
Andrew Truckle

Tôi đã so sánh khả năng đơn giản hóa biểu thức của trình biên dịch với sự đơn giản hóa của bạn như một tham chiếu: trình khám phá trình biên dịch . gcc đã không tìm thấy phiên bản tối ưu của bạn nhưng giải pháp của nó vẫn tốt. Clang và MSVC dường như không thực hiện bất kỳ đơn giản hóa biểu thức boolean nào.
Oliv

1
@AndrewTruckle: lưu ý rằng nếu bạn cần một phiên bản dễ đọc hơn, vui lòng nói như vậy. Bạn đã nói "đơn giản hóa", nhưng bạn chấp nhận một phiên bản thậm chí còn dài dòng hơn phiên bản gốc của bạn.
geza

1
simplethực sự là một thuật ngữ mơ hồ. Nhiều người hiểu nó trong ngữ cảnh này vì nó đơn giản hơn cho nhà phát triển hiểu và không cho trình biên dịch tạo mã, vì vậy nhiều chi tiết hơn thực sự có thể đơn giản hơn.
Zdeslav Vojkovic

1
@IsmaelMiguel: khi một công thức logic được tối ưu hóa cho số thuật ngữ, nghĩa ban đầu thường bị mất. Nhưng người ta có thể đặt một bình luận xung quanh nó, vì vậy nó rõ ràng là gì. Thậm chí, đối với câu trả lời được chấp nhận, một bình luận sẽ không gây hại.
geza

12

Cân nhắc dịch các bảng của bạn trực tiếp nhất có thể vào chương trình của bạn. Điều khiển chương trình dựa trên bàn, thay vì bắt chước nó bằng logic.

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

hiện nay

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

điều này trực tiếp mã hóa bảng sự thật của bạn vào trình biên dịch.

Ví dụ trực tiếp .

Bạn cũng có thể sử dụng std::any_oftrực tiếp:

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

trình biên dịch có thể nội dòng mã và loại bỏ bất kỳ sự lặp lại nào và xây dựng logic của riêng nó cho bạn. Trong khi đó, mã của bạn phản ánh chính xác cách bạn giải quyết vấn đề.


Phiên bản đầu tiên rất dễ đọc và rất dễ bảo trì, tôi thực sự thích nó. Cái thứ hai khó đọc hơn, ít nhất là đối với tôi, và yêu cầu trình độ kỹ năng c ++ có thể trên mức trung bình, chắc chắn hơn tôi. Không phải ai cũng viết được. Chỉ cần học Somethin mới, nhờ
Gian Paolo

11

Tôi chỉ cung cấp câu trả lời của tôi ở đây như trong các nhận xét mà ai đó đề xuất để hiển thị giải pháp của tôi. Tôi muốn cảm ơn mọi người vì những hiểu biết của họ.

Cuối cùng, tôi đã chọn thêm ba booleanphương pháp "kịch bản" mới:

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

Sau đó, tôi có thể áp dụng những quy trình xác thực của mình như thế này:

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

Trong ứng dụng trực tiếp của tôi, 4 giá trị bool thực sự được trích xuất từ ​​một DWORDgiá trị có 4 giá trị được mã hóa vào đó.

Một lần nữa xin cảm ơn tất cả mọi người.


1
Cảm ơn vì đã chia sẻ giải pháp. :) Nó thực sự tốt hơn phức tạp nếu điều kiện địa ngục. Có lẽ bạn vẫn có thể đặt tên INCLUDE_ITEM1vv theo cách tốt hơn và bạn đều tốt. :)
Hardik Modha

1
@HardikModha À, về mặt kỹ thuật thì chúng là "Vật phẩm dành cho sinh viên" và cờ là để cho biết liệu chúng có được "đưa vào" hay không. Vì vậy, tôi nghĩ rằng cái tên, mặc dù nghe có vẻ chung chung, thực sự có ý nghĩa trong bối cảnh này. :)
Andrew Truckle

11

Tôi không thấy bất kỳ câu trả lời nào nói rằng hãy đặt tên cho các tình huống, mặc dù giải pháp của OP thực hiện chính xác điều đó.

Đối với tôi, tốt nhất là nên đóng gói chú thích của mỗi kịch bản thành một tên biến hoặc tên hàm. Bạn có nhiều khả năng bỏ qua một nhận xét hơn là một cái tên và nếu logic của bạn thay đổi trong tương lai, bạn có nhiều khả năng thay đổi tên hơn là một nhận xét. Bạn không thể cấu trúc lại một bình luận.

Nếu bạn có kế hoạch sử dụng lại các tình huống này bên ngoài chức năng của mình (hoặc có thể muốn), thì hãy tạo một hàm cho biết những gì nó đánh giá ( constexpr/ noexcepttùy chọn nhưng được khuyến nghị):

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

Tạo các phương thức lớp này nếu có thể (như trong giải pháp của OP). Bạn có thể sử dụng các biến bên trong hàm của mình nếu bạn không nghĩ rằng mình sẽ sử dụng lại logic:

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;

Trình biên dịch rất có thể sẽ sắp xếp rằng nếu bValue1 là false thì tất cả các kịch bản đều là false. Đừng lo lắng về việc làm cho nó nhanh, chỉ cần chính xác và dễ đọc. Nếu bạn lập hồ sơ cho mã của mình và thấy đây là một nút cổ chai vì trình biên dịch đã tạo mã tối ưu phụ ở -O2 hoặc cao hơn thì hãy thử viết lại nó.


Tôi thích giải pháp này hơn một chút so với giải pháp (vốn đã rất hay) của Gian Paolo: Nó tránh được luồng điều khiển và việc sử dụng một biến bị ghi đè - kiểu chức năng hơn.
Dirk Herrmann

9

AC / C ++ cách

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

Cách tiếp cận này có thể mở rộng vì nếu số lượng các điều kiện hợp lệ tăng lên, bạn dễ dàng chỉ cần thêm nhiều điều kiện trong số đó vào danh sách kịch bản.


Tôi khá chắc chắn rằng điều này là sai, mặc dù. Nó giả định rằng trình biên dịch chỉ sử dụng một biểu diễn nhị phân duy nhất cho true. Một trình biên dịch sử dụng "bất kỳ điều gì khác 0 đều đúng" khiến mã này bị lỗi. Lưu ý rằng truephải chuyển đổi sang 1, nó chỉ không cần phải được lưu trữ như vậy.
MSalters

@MSalters, tnx, tôi hiểu ý bạn và tôi biết điều đó, đại loại là 2 is not equal to true but evaluates to true, mã của tôi không bắt buộc int 1 = truevà hoạt động miễn là tất cả các mã true được chuyển đổi thành cùng một giá trị int, VẬY, đây là câu hỏi của tôi: Tại sao trình biên dịch lại hành động ngẫu nhiên khi chuyển đổi đúng với int cơ bản, Bạn có thể vui lòng giải thích thêm được không?
hessam hedieh 3/1218

Thực hiện một memcmpđể kiểm tra các điều kiện boolean không phải là cách C ++ và tôi khá nghi ngờ rằng đó cũng là một cách C đã được thiết lập.
Konrad Rudolph

@hessamhedieh: Vấn đề trong logic của bạn là "chuyển đổi true thành int cơ bản". Đó là không bao các trình biên dịch làm việc,
MSalters

Mã của bạn tăng độ phức tạp từ O (1) lên O (n). Không phải là một cách để sử dụng trong bất kỳ ngôn ngữ nào - hãy bỏ C / C ++ sang một bên.
mabel

9

Dễ dàng nhận thấy rằng hai kịch bản đầu tiên tương tự nhau - chúng có chung hầu hết các điều kiện. Nếu bạn muốn chọn tình huống hiện tại, bạn có thể viết nó như sau (đó là giải pháp được sửa đổi của @ gian-paolo ):

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

Đi xa hơn, bạn có thể nhận thấy rằng boolean đầu tiên cần phải luôn đúng, đây là điều kiện đầu vào, vì vậy bạn có thể kết thúc bằng:

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
}

Thậm chí, bạn có thể thấy rõ ràng rằng bValue2 và bValue3 phần nào được kết nối với nhau - bạn có thể trích xuất trạng thái của chúng thành một số hàm hoặc biến bên ngoài với tên thích hợp hơn (điều này không phải lúc nào cũng dễ dàng hoặc phù hợp):

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
}

Làm theo cách này có một số ưu điểm và nhược điểm:

  • các điều kiện nhỏ hơn, vì vậy sẽ dễ dàng lập luận về chúng hơn,
  • thật dễ dàng hơn để đổi tên đẹp để làm cho các điều kiện này dễ hiểu hơn,
  • nhưng, họ yêu cầu phải hiểu phạm vi,
  • hơn nữa nó cứng nhắc hơn

Nếu bạn dự đoán rằng sẽ có những thay đổi đối với logic trên, bạn nên sử dụng cách tiếp cận đơn giản hơn như được trình bày bởi @ gian-paolo .

Mặt khác, nếu các điều kiện này được thiết lập tốt và là loại "quy tắc vững chắc" sẽ không bao giờ thay đổi, hãy xem xét đoạn mã cuối cùng của tôi.


7

Theo gợi ý của mch, bạn có thể làm:

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

trong đó dòng đầu tiên bao gồm hai trường hợp tốt đầu tiên và dòng thứ hai bao gồm trường hợp cuối cùng.

Live Demo, nơi tôi đã chơi xung quanh và nó vượt qua các trường hợp của bạn.


7

Một biến thể nhỏ về câu trả lời hay của @ GianPaolo, mà một số người có thể thấy dễ đọc hơn:

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

Mọi câu trả lời đều quá phức tạp và khó đọc. Giải pháp tốt nhất cho điều này là một switch()tuyên bố. Nó vừa dễ đọc vừa giúp việc thêm / sửa đổi các trường hợp bổ sung trở nên đơn giản. Các trình biên dịch cũng rất giỏi trong việc tối ưu hóa các switch()câu lệnh.

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

Tất nhiên, bạn có thể sử dụng hằng số và HOẶC chúng cùng nhau trong các casecâu lệnh để dễ đọc hơn.


Là một lập trình viên C cũ, tôi muốn xác định macro "PackBools" và sử dụng macro đó cho cả "switch (PackBools (a, b, c, d))" và cho các trường hợp, ví dụ: trực tiếp "case PackBools (true , true ...) "hoặc định nghĩa chúng dưới dạng hằng số cục bộ.eg" const unsigned int script1 = PackBools (true, true ...); "
Simon F

6

Tôi cũng sẽ sử dụng các biến phím tắt để rõ ràng. Như đã lưu ý trước đó, kịch bản 1 bằng với kịch bản 2, vì giá trị của bValue4 không ảnh hưởng đến sự thật của hai kịch bản đó.

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

thì biểu thức của bạn sẽ là:

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

Đặt tên có ý nghĩa cho các biến MAJORTRUE và MAJORFALSE (cũng như trên thực tế cho bValue * vars) sẽ giúp ích rất nhiều cho khả năng đọc và bảo trì.


6

Tập trung vào tính dễ đọc của vấn đề, không phải câu lệnh "nếu" cụ thể.

Mặc dù điều này sẽ tạo ra nhiều dòng mã hơn và một số có thể coi nó là quá mức cần thiết hoặc không cần thiết. Tôi đề nghị rằng việc trừu tượng hóa các kịch bản của bạn khỏi các boolean cụ thể là cách tốt nhất để duy trì khả năng đọc.

Bằng cách chia nhỏ mọi thứ thành các lớp (thoải mái chỉ sử dụng các hàm hoặc bất kỳ công cụ nào khác mà bạn thích) với những cái tên dễ hiểu - chúng tôi có thể dễ dàng chỉ ra ý nghĩa đằng sau mỗi kịch bản. Quan trọng hơn, trong một hệ thống có nhiều bộ phận chuyển động - việc duy trì và tham gia vào hệ thống hiện có của bạn sẽ dễ dàng hơn (một lần nữa, mặc dù có bao nhiêu mã bổ sung được yêu cầu).

#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
Tại một số điểm, sự dài dòng bắt đầu gây hại cho khả năng đọc. Tôi nghĩ rằng điều này đã đi quá xa.
JollyJoker

2
@JollyJoker Tôi thực sự đồng ý trong tình huống cụ thể này - tuy nhiên, cảm giác ruột thịt của tôi từ cách OP đặt tên cho mọi thứ cực kỳ chung chung, là mã "thực" của họ có thể phức tạp hơn rất nhiều so với ví dụ mà họ đã đưa ra. Thực sự, tôi chỉ muốn đưa sự thay thế này ra khỏi đó, vì đó là cách tôi cấu trúc nó cho một thứ phức tạp / liên quan hơn nhiều. Nhưng bạn nói đúng - đối với ví dụ cụ thể về OP, nó quá dài dòng và làm cho vấn đề trở nên tồi tệ hơn.

5

Nó phụ thuộc vào những gì họ đại diện.

Ví dụ: nếu 1 là khóa, và 23 là hai người phải đồng ý (ngoại trừ nếu họ đồng ý NOTthì họ cần người thứ ba - 4 - xác nhận) thì người đọc dễ đọc nhất có thể là:

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

theo yêu cầu phổ biến:

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

2
Bạn có thể đúng, nhưng việc sử dụng các con số để minh họa quan điểm của bạn sẽ làm mất đi câu trả lời của bạn. Thử sử dụng tên mô tả.
jxh

1
@jxh Đó là những con số OP đã sử dụng. Tôi vừa gỡ bỏ bValue.
ispiro

@jxh Tôi hy vọng nó tốt hơn bây giờ.
ispiro

4

Thao tác bitwise trông rất sạch sẽ và dễ hiểu.

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

1
So sánh bitwise có vẻ dễ đọc đối với tôi. Mặt khác, thành phần có vẻ nhân tạo.
xtofl

3

Tôi đang ký hiệu a, b, c, d cho rõ ràng và A, B, C, D để bổ sung

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

Phương trình

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

Sử dụng bất kỳ phương trình nào phù hợp với bạn.


3
If (!bValue1 || (bValue2 != bValue3) || (!bValue4 && bValue2))
{
// you have a problem
}
  • b1 phải luôn đúng
  • b2 phải luôn bằng b3
  • và b4 không thể sai nếu b2 (và b3) đúng

đơn giản


3

Chỉ là sở thích cá nhân đối với câu trả lời được chấp nhận, nhưng tôi sẽ viết:

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

Đầu tiên, giả sử bạn chỉ có thể sửa đổi kiểm tra kịch bản, tôi sẽ tập trung vào khả năng đọc và chỉ gói kiểm tra trong một hàm để bạn có thể gọi if(ScenarioA()).


Bây giờ, giả sử bạn thực sự muốn / cần tối ưu hóa điều này, tôi khuyên bạn nên chuyển đổi các Boolean được liên kết chặt chẽ thành các số nguyên không đổi và sử dụng các toán tử bit trên chúng

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
}

Điều này làm cho việc diễn đạt các kịch bản trở nên dễ dàng như liệt kê những gì là một phần của nó, cho phép bạn sử dụng câu lệnh chuyển đổi để chuyển đến điều kiện phù hợp và gây nhầm lẫn cho các nhà phát triển đồng nghiệp chưa từng thấy điều này trước đây. (C # RegexOptions sử dụng mẫu này để đặt cờ, tôi không biết có ví dụ về thư viện c ++ không)


Trên thực tế, tôi không sử dụng bốn giá trị bool mà là một DWORD với bốn BOOLS được nhúng. Quá muộn để thay đổi nó ngay bây giờ. Nhưng cảm ơn cho đề nghị của bạn.
Andrew Truckle

2

Các chữ lồng nhau ifcó thể dễ đọc hơn đối với một số người. Đây là phiên bản của tôi

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

Cá nhân tôi thường tránh lồng các câu lệnh if nếu có thể. Mặc dù trường hợp này rất hay và dễ đọc, nhưng một khi các khả năng mới được thêm vào, phần lồng có thể trở nên rất khó đọc. Nhưng nếu các tình huống không bao giờ thay đổi, nó chắc chắn là một giải pháp tốt và dễ đọc.
Dnomyar96

@ Dnomyar96 tôi đồng ý. Cá nhân tôi cũng tránh ifs lồng nhau. Đôi khi nếu logic phức tạp, tôi sẽ dễ dàng hiểu logic hơn bằng cách chia nhỏ nó thành nhiều phần. Ví dụ: khi bạn nhập bValue1khối, bạn có thể coi mọi thứ trong đó như một trang mới trong quá trình tinh thần của mình. Tôi cá rằng cách tiếp cận vấn đề có thể rất cá nhân hoặc thậm chí là văn hóa.
sardok

1

Một số câu trả lời đúng đã được đưa ra cho câu hỏi này, nhưng tôi sẽ có một cái nhìn khác: nếu mã trông quá phức tạp, có gì đó không ổn . Mã sẽ khó gỡ lỗi và có nhiều khả năng là "chỉ sử dụng một lần".

Trong cuộc sống thực, khi chúng ta tìm thấy một tình huống như thế này:

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

Khi bốn trạng thái được kết nối bằng một mẫu chính xác như vậy, chúng ta đang xử lý cấu hình của một số "thực thể" trong mô hình của chúng ta .

Một phép ẩn dụ cực đoan là cách chúng ta mô tả một "con người" trong một mô hình, nếu chúng ta không nhận thức được sự tồn tại của họ như những thực thể đơn nhất với các thành phần được kết nối thành các bậc tự do cụ thể: chúng ta sẽ phải mô tả các trạng thái độc lập của "thân", "cánh tay", "chân" và "đầu" khiến việc hiểu hệ thống được mô tả sẽ trở nên phức tạp. Kết quả ngay lập tức sẽ là các biểu thức boolean phức tạp không tự nhiên.

Rõ ràng, cách để giảm độ phức tạp là trừu tượng hóa và một công cụ được lựa chọn trong c ++ là mô hình đối tượng .

Vậy câu hỏi đặt ra là: tại sao lại có một khuôn mẫu như vậy? Đây là gì và nó đại diện cho điều gì?

Vì chúng ta không biết câu trả lời, chúng ta có thể quay lại với một trừu tượng toán học: mảng : chúng ta có ba kịch bản, mỗi kịch bản bây giờ là một mảng.

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

Tại thời điểm đó bạn có cấu hình ban đầu của mình. như một mảng. Ví dụ: std::arraycó một toán tử bình đẳng:

Tại thời điểm đó, cú pháp của bạn trở thành:

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

}

Đúng như câu trả lời của Gian Paolo, nó ngắn gọn, rõ ràng và có thể dễ dàng xác minh / gỡ lỗi. Trong trường hợp này, chúng tôi đã ủy quyền chi tiết của các biểu thức boolean cho trình biên dịch.


1

Bạn sẽ không phải lo lắng về sự kết hợp không hợp lệ của các cờ boolean nếu bạn loại bỏ các cờ boolean.

Các giá trị được chấp nhận là:

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

Bạn rõ ràng có ba trạng thái (kịch bản). Tốt hơn là nên mô hình hóa điều đó và lấy các thuộc tính boolean từ các trạng thái đó, chứ không phải ngược lại.

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

Đây chắc chắn là nhiều mã hơn trong câu trả lời của Gian Paolo , nhưng tùy thuộc vào tình huống của bạn, điều này có thể dễ bảo trì hơn nhiều:

  • Có một tập hợp các chức năng trung tâm để sửa đổi nếu các thuộc tính hoặc kịch bản boolean bổ sung được thêm vào.
    • Thêm thuộc tính chỉ yêu cầu thêm một chức năng duy nhất.
    • Nếu thêm một kịch bản, việc bật cảnh báo trình biên dịch về enumcác trường hợp switchchưa được xử lý trong các câu lệnh sẽ bắt các trình nhận thuộc tính không xử lý tình huống đó.
  • Nếu bạn cần sửa đổi động các thuộc tính boolean, bạn không cần xác thực lại các kết hợp của chúng ở mọi nơi. Thay vì chuyển đổi các cờ boolean riêng lẻ (có thể dẫn đến các kết hợp cờ không hợp lệ), thay vào đó bạn sẽ có một máy trạng thái chuyển đổi từ kịch bản này sang kịch bản khác.

Cách tiếp cận này cũng có lợi ích phụ là rất hiệu quả.


0

2 xu của tôi: khai báo một biến tổng (số nguyên) để

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

Kiểm tra tổng với các điều kiện bạn muốn và thế là xong. Bằng cách này, bạn có thể dễ dàng thêm nhiều điều kiện hơn trong tương lai, giữ cho nó khá dễ đọc.


0

Câu trả lời được chấp nhận là tốt khi bạn chỉ có 3 trường hợp và trong đó logic cho mỗi trường hợp rất đơn giản.

Nhưng nếu logic cho mỗi trường hợp phức tạp hơn hoặc có nhiều trường hợp hơn, thì một lựa chọn tốt hơn nhiều là sử dụng chuỗi trách nhiệm mẫu thiết kế .

Bạn tạo một BaseValidatorchứa một tham chiếu đến a BaseValidatorvà một phương thức validatevà một phương thức để gọi xác thực trên trình xác thực được tham chiếu.

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

Sau đó, bạn tạo một số lớp con kế thừa từ BaseValidator, ghi đè validatephương thức với logic cần thiết cho mỗi trình xác thực.

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

Sau đó, việc sử dụng nó rất đơn giản, khởi tạo từng trình xác thực của bạn và đặt từng trình xác thực này làm thư mục gốc của những trình xác thực khác:

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

Về bản chất, mỗi trường hợp xác thực có lớp riêng của nó chịu trách nhiệm (a) xác định xem việc xác thực có khớp với trường hợp đó hay không và (b) gửi xác thực cho người khác trong chuỗi nếu không.

Xin lưu ý rằng tôi không quen thuộc với C ++. Tôi đã cố gắng khớp cú pháp từ một số ví dụ mà tôi tìm thấy trực tuyến, nhưng nếu điều này không hoạt động, hãy coi nó giống như mã giả hơn. Tôi cũng có một ví dụ Python hoạt động hoàn chỉnh bên dưới có thể được sử dụng làm cơ sở nếu được ưu tiên.

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

Một lần nữa, bạn có thể thấy điều này quá mức cần thiết cho ví dụ cụ thể của bạn, nhưng nó tạo ra mã sạch hơn nhiều nếu bạn gặp phải một loạt trường hợp phức tạp hơn nhiều cần được đáp ứng.


-2

Một cách tiếp cận đơn giản là tìm câu trả lời mà bạn cho là có thể chấp nhận được.

Có = (boolean1 && boolean2 && boolean3 && boolean4) + + ...

Bây giờ nếu có thể, hãy đơn giản hóa phương trình bằng cách sử dụng đại số boolean.

như trong trường hợp này, accept1 và 2 kết hợp thành (boolean1 && boolean2 && boolean3).

Do đó, câu trả lời cuối cùng là:

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

-3

sử dụng trường 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) {...}

Tái bút :

Đó là một điều đáng tiếc lớn đối với CPPers '. Nhưng, UB không phải là nỗi lo của tôi, hãy kiểm tra nó tại http://coliru.stacked-crooked.com/a/2b556abfc28574a1 .


2
Điều này gây ra UB do truy cập vào một trường liên hiệp không hoạt động.
HolyBlackCat

Về mặt hình thức, đó là UB trong C ++, bạn không thể đặt một thành viên của liên minh và đọc từ một thành viên khác. Về mặt kỹ thuật, có thể tốt hơn nếu triển khai getters \ setters được tạo mẫu cho các bit có giá trị tích phân.
Swift - Friday Pie

Tôi nghĩ rằng hành vi sẽ chuyển sang được Xác định bởi Thực thi nếu một người chuyển đổi địa chỉ của liên minh thành một unsigned char*, mặc dù tôi nghĩ chỉ cần sử dụng một cái gì đó như thế ((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1có lẽ sẽ hiệu quả hơn.
supercat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.