Nếu điều kiện A được khớp, điều kiện B cần được khớp để thực hiện hành động C


148

Câu hỏi của tôi là:

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

Có thể chỉ viết mã hành động C một lần thay vì hai lần?

Làm thế nào để đơn giản hóa nó?


56
Đặt mã cho "hành động C" trong một chức năng?
CinCout

26
Điều này thật đáng buồn vì điều này không thực sự liên quan đến câu hỏi C ++ đã gửi tới HNQ: /
YSC

2
Cảm ơn mọi người đã giúp tôi! Ban đầu, tôi chỉ muốn đảm bảo mọi thứ đều ổn nên tôi đã sử dụng if lồng nhau. Đó là bởi vì đây là cách đơn giản nhất tôi đoán. Tôi sẽ cố gắng nỗ lực nhiều hơn sau khi đặt câu hỏi vào lần tới. Chúc mọi người có một ngày tốt lành :)
starf15h

13
Đó là một chiến lược rất tốt: viết mã hoạt động trước, sau đó lo lắng về việc làm cho nó thanh lịch và hiệu quả sau này.
Code-Apprentice

3
@Tim tôi đã đăng nó như một câu trả lời. Bên cạnh đó, thật buồn khi thấy ít phiếu hơn ở đó.
CinCout

Câu trả lời:


400

Bước đầu tiên của bạn trong các loại vấn đề này là luôn tạo một bảng logic.

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

Khi bạn đã tạo bảng, giải pháp rõ ràng.

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

Hãy lưu ý rằng logic này, trong khi ngắn hơn, có thể khó để các lập trình viên trong tương lai duy trì.


35
Tôi thực sự thích rằng bạn đã chỉ ra bảng chân lý để giúp OP hiểu cách tự phát triển điều này. Bạn có thể tiến thêm một bước này và giải thích làm thế nào bạn có được biểu thức boolean từ bảng chân lý? Đối với một người mới lập trình và logic boolean, điều này có lẽ không rõ ràng chút nào.
Code-Apprentice

14
Nếu việc đánh giá Bcó tác dụng phụ, bảng logic phải tính đến điều đó.
Yakk - Adam Nevraumont

79
@Yakk Câu trả lời của tôi không đề cập đến tác dụng phụ vì hai lý do. Đầu tiên, giải pháp không (trùng hợp) có hành vi tác dụng phụ chính xác. Thứ hai, và quan trọng hơn, A và B có tác dụng phụ sẽ là mã xấu và một cuộc thảo luận về trường hợp bên lề đó sẽ là một sự phân tâm cho một câu hỏi cơ bản về logic boolean.
Câu hỏi

52
Có lẽ đáng chú ý, trong trường hợp trường A && !Bhợp là không có: !(A && !B)tương đương với !A || Bđiều đó có nghĩa là bạn có thể làm if (!A || B) { /* do action C */ }và tránh một khối trống.
KRyan

54
Nếu if (A && !B)thực sự khó khăn cho các lập trình viên tương lai để duy trì, thì thực sự không có gì giúp họ.
Ray

65

Bạn có hai lựa chọn:

  1. Viết hàm thực hiện "hành động C".

  2. Sắp xếp lại logic của bạn để bạn không có quá nhiều câu lệnh if lồng nhau. Hãy tự hỏi những điều kiện gây ra "hành động C" xảy ra. Đối với tôi có vẻ như nó xảy ra khi "điều kiện B" là đúng hoặc "điều kiện A" là sai. Chúng ta có thể viết điều này là "KHÔNG A HOẶC B". Dịch mã này sang mã C, chúng tôi nhận được

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

Để tìm hiểu thêm về các loại biểu thức này, tôi đề nghị googling "đại số boolean", "logic vị ngữ" và "tính toán vị ngữ". Đây là những chủ đề toán học sâu sắc. Bạn không cần phải học tất cả, chỉ là những điều cơ bản.

Bạn cũng nên tìm hiểu về "đánh giá ngắn mạch". Bởi vì điều này, thứ tự của các biểu thức là quan trọng để sao chép chính xác logic ban đầu của bạn. Mặc dù B || !Atương đương về mặt logic, sử dụng điều này làm điều kiện sẽ thực thi "hành động C" khi Bđúng bất kể giá trị của A.


15
@Yakk Xem Luật của deMorgan.
Code-Apprentice

@ Code-Apprentice Xin hãy tha thứ cho suy nghĩ logic tồi tệ của tôi. Tôi muốn hỏi liệu có sự khác biệt nào giữa (! A || B) và (A &&! B) không. Có vẻ như cả hai đều ổn cho vấn đề của tôi. Ý tôi là cách tiếp cận của bạn và của Câu hỏi.
starf15h

6
@ Starf15h Có một sự khác biệt quan trọng khác: trong đó "hành động C" được thực thi. Sự khác biệt này làm cho hai giải pháp của chúng tôi chính xác tương đương. Tôi đề nghị bạn google "Luật của deMorgan" sẽ giúp bạn hiểu những gì đang diễn ra ở đây.
Code-Apprentice

5
Hai giải pháp hoàn toàn tương đương, nhưng có thể có một sự khác biệt thực tế tùy thuộc vào chính xác ...là gì . Nếu nó hoàn toàn không có gì (nghĩa là, do do điều kiện này được đáp ứng; nếu không thì không có gì khác), thì đây rõ ràng là giải pháp ưu việt, vì elsecâu lệnh đơn giản chỉ có thể bị loại bỏ hoàn toàn.
Janus Bahs Jacquet

1
Ngoài ra, tùy thuộc vào tên của A và B, sự sắp xếp này có thể dễ đọc hơn hoặc dễ đọc hơn đối với con người so với sự sắp xếp của Câu hỏi.
Michael - Clay Shirky ở đâu

15

Bạn có thể đơn giản hóa tuyên bố như thế này:

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

Mặt khác, đặt mã cho 'C' trong một chức năng riêng biệt và gọi nó:

DoActionC()
{
    ....
    // code for Action C
}
if (condition A)
{
    if(condition B)
      {
         DoActionC(); // call the function
      }
    else
      ...
}
else
{
   DoActionC(); // call the function
}

7
Hoặc đơn giản hơnif (!A || B)
TAS

2
Theo logic, ((A && B) ||! A) tương đương với (B ||! A)
Code-Apprentice

@ Code-Apprentice B || !Asẽ dẫn đến truechỉ khi Btrue, mà không thực sự kiểm tra Ado ngắn mạch
CinCout

1
@CinCout Điểm tốt. Trong khi tuyên bố của tôi vẫn đúng theo quan điểm logic boolean lý thuyết, tôi đã không tính đến tính thực tiễn của các toán tử boolean ngắn mạch. May mắn thay, câu trả lời của riêng tôi có thứ tự chính xác.
Code-Apprentice

1
Vì vậy, từ góc độ logic, thứ tự không quan trọng. Tuy nhiên, từ quan điểm bảo trì và dễ đọc, có thể có một sự khác biệt lớn tùy thuộc vào chính xác Avà đại diện Bcho điều gì .
Code-Apprentice

14

Trong một ngôn ngữ có khớp mẫu, bạn có thể diễn đạt giải pháp theo cách phản ánh trực tiếp hơn bảng sự thật trong câu trả lời của Câu hỏi.

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

Nếu bạn không quen với cú pháp, mỗi mẫu được biểu thị bằng một | theo sau là các giá trị khớp với (a, b) và dấu gạch dưới được sử dụng làm ký tự đại diện để có nghĩa là "bất kỳ giá trị nào khác". Vì trường hợp duy nhất mà chúng tôi muốn làm gì đó ngoài hành động c là khi a đúng và b sai, chúng tôi tuyên bố rõ ràng các giá trị đó là mẫu đầu tiên (đúng, sai) và sau đó làm bất cứ điều gì nên làm trong trường hợp đó. Trong tất cả các trường hợp khác, chúng tôi rơi vào mẫu "ký tự đại diện" và thực hiện hành động c.


10

Báo cáo vấn đề:

Nếu điều kiện A được khớp, điều kiện B cần được khớp để thực hiện hành động C

mô tả hàm ý : A ngụ ý B , một mệnh đề logic tương đương với !A || B(như đã đề cập trong các câu trả lời khác):

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

if (implies(/* condition A */,
            /* condition B */))
{
    /* do action C */
}

Có lẽ đánh dấu nó inlinecho C và constexprcũng cho C ++?
einpoklum

@einpoklum Tôi không hiểu được một số chi tiết đó vì câu hỏi này không thực sự chỉ định ngôn ngữ (nhưng đã đưa ra một ví dụ với cú pháp giống C), vì vậy tôi đã đưa ra câu trả lời với cú pháp giống C. Cá nhân tôi sẽ sử dụng một macro để điều kiện B không được đánh giá một cách không cần thiết.
jamesdlin

6

Ugh, điều này cũng làm tôi vấp ngã, nhưng như Code-Apprentice đã chỉ ra, chúng tôi đảm bảo cần phải do action Cchạy hoặc chạy elsekhối lồng nhau , do đó mã có thể được đơn giản hóa thành:

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

Đây là cách chúng tôi nhấn 3 trường hợp:

  1. Yêu cầu lồng nhau do action Ctrong logic câu hỏi của bạn condition Acondition Bphải là true- Trong logic này, nếu chúng ta đạt đến thuật ngữ thứ 2 trong phần ifsau thì chúng ta biết đó condition Atruetất cả những gì chúng ta cần đánh giá đó condition Btrue
  2. elseChặn- lồng trong logic của câu hỏi của bạn bắt buộc condition Aphải truecondition Bphải false- Cách duy nhất mà chúng ta có thể đạt tới else-block trong logic này sẽ là nếu condition Ađã truecondition Bđangfalse
  3. Câu hỏi bên ngoài elsetrong logic câu hỏi của bạn bắt buộc condition Aphải có false- Trong logic này nếu condition Asai, chúng tôi cũngdo action C

Đạo cụ để Code-Apprentice cho tôi nói thẳng ra đây. Tôi đề nghị chấp nhận câu trả lời của anh ấy , vì anh ấy đã trình bày chính xác mà không cần chỉnh sửa: /


2
Lưu ý rằng "điều kiện A" không cần phải đánh giá lại. Trong C ++, chúng ta có luật trung gian bị loại trừ. Nếu "không phải điều kiện A" là sai, thì "điều kiện A" nhất thiết phải đúng.
Code-Apprentice

1
Do đánh giá ngắn mạch, Bsẽ chỉ được đánh giá nếu !Asai. Vì vậy, cả hai phải thất bại để thực hiện các elsebáo cáo.
Code-Apprentice

Ngay cả khi không có đánh giá ngắn mạch !A || Blà sai chính xác khi cả hai !ABđều sai. Do đó, Asẽ đúng khi elsethực thi. Không cần phải đánh giá lại A.
Code-Apprentice

@ Code-Apprentice Vâng, quan sát tuyệt vời, tôi đã sửa câu trả lời của mình, nhưng đề nghị bạn được chấp nhận. Tôi chỉ đang cố gắng giải thích những gì bạn đã đưa ra.
Jonathan Mee

Tôi ước tôi có thể cho bạn một phiếu bầu khác để giải thích từng trường hợp.
Code-Apprentice

6

Trong khái niệm logic, bạn có thể giải quyết vấn đề này như sau:

f = ab +! a
f =?

Như một vấn đề đã được chứng minh, điều này dẫn đến f = !a + b. Có một số cách để chứng minh vấn đề như bảng chân lý, Bản đồ Karnaugh , v.v.

Vì vậy, trong các ngôn ngữ dựa trên C, bạn có thể sử dụng như sau:

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

PS: Karnaugh Map cũng được sử dụng cho một loạt các điều kiện phức tạp hơn. Đây là một phương pháp đơn giản hóa các biểu thức đại số Boolean.


6

Mặc dù đã có câu trả lời tốt, tôi nghĩ rằng cách tiếp cận này có thể còn trực quan hơn đối với người mới biết về đại số Boolean sau đó để đánh giá một bảng chân lý.

Điều đầu tiên bạn muốn làm là nhìn, trong những điều kiện bạn muốn thực thi C. Đây là trường hợp khi (a & b). Còn khi nào !a. Vì vậy, bạn có (a & b) | !a.

Nếu bạn muốn giảm thiểu bạn có thể tiếp tục. Giống như trong số học "bình thường", bạn có thể nhân ra.

(a & b) | !a = (a | !a) & (b | !a). một | ! a luôn luôn đúng, vì vậy bạn chỉ có thể gạch bỏ nó, điều này khiến bạn có kết quả tối thiểu : b | !a. Trong trường hợp thứ tự tạo ra sự khác biệt, bởi vì bạn chỉ muốn kiểm tra b nếu! A là đúng (ví dụ khi! A là kiểm tra nullpulum và b là một thao tác trên con trỏ như @LordFarquaad đã chỉ ra trong nhận xét của anh ấy), bạn có thể muốn chuyển đổi hai.

Trường hợp khác (/ * ... * /) sẽ luôn được thực thi khi c không được thực thi, vì vậy chúng ta chỉ có thể đặt nó trong trường hợp khác.

Một điều đáng nói nữa là nó có thể có ý nghĩa trong việc đưa hành động c vào một phương thức.

Mà để lại cho chúng tôi mã sau đây:

if (!A || B)
{
    doActionC()  // execute method which does action C
}
else
{
   /* ... */ // what ever happens here, you might want to put it into a method, too.
}

Bằng cách này, bạn cũng có thể giảm thiểu các thuật ngữ với nhiều toán hạng hơn, nhanh chóng trở nên xấu xí với các bảng chân lý. Một cách tiếp cận tốt khác là bản đồ Karnaugh. Nhưng tôi sẽ không đi sâu vào vấn đề này ngay bây giờ.


4

Để làm cho mã trông giống văn bản hơn, hãy sử dụng cờ boolean. Nếu logic đặc biệt tối nghĩa, hãy thêm ý kiến.

bool do_action_C;

// Determine whether we need to do action C or just do the "..." action
// If condition A is matched, condition B needs to be matched in order to do action C
if (/* condition A */)
{
    if(/* condition B */)
      do_action_C = true; // have to do action C because blah
    else
      do_action_C = false; // no need to do action C because blarg
}
else
{
  do_action_C = true; // A is false, so obviously have to do action C
}

if (do_action_C)
  {
     DoActionC(); // call the function
  }
else
  {
  ...
  }

3
if((A && B ) || !A)
{
  //do C
}
else if(!B)
{
  //...
}

2

Tôi sẽ trích xuất C sang một phương thức, và sau đó thoát khỏi hàm càng sớm càng tốt trong mọi trường hợp. elsemệnh đề với một điều duy nhất ở cuối nên hầu như luôn luôn được đảo ngược nếu có thể. Đây là một ví dụ từng bước:

Trích xuất C:

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

Đảo ngược đầu tiên ifđể thoát khỏi đầu tiên else:

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

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

Loại bỏ thứ hai else:

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

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

D();

Và sau đó bạn có thể nhận thấy rằng hai trường hợp có cùng một cơ thể và có thể được kết hợp:

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

D();

Những thứ tùy chọn để cải thiện sẽ là:

  • phụ thuộc vào ngữ cảnh, nhưng nếu !A || Bkhó hiểu, hãy trích xuất nó thành một hoặc nhiều biến để giải thích ý định

  • Bất kỳ trường hợp nào C()hoặc D()là trường hợp không ngoại lệ sẽ diễn ra sau cùng, vì vậy nếu D()là trường hợp ngoại lệ, thì hãy đảo ngược iflần cuối cùng


2

Sử dụng cờ cũng có thể giải quyết vấn đề này

int flag = 1; 
if ( condition A ) {
    flag = 2;
    if( condition B ) {
        flag = 3;
    }
}
if(flag != 2) { 
    do action C 
}
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.