Tạo kiểu hợp chất AND / OR nếu câu lệnh


13

Làm thế nào để bạn tạo kiểu phức hợp AND / OR nếu các câu lệnh cho khả năng đọc tối đa? Làm thế nào để bạn thụt lề và nơi bạn đặt các ngắt dòng? Tình hình cụ thể của tôi là một cái gì đó như sau. Nó chắc chắn tốt hơn là đập mọi thứ thành một dòng, nhưng trông vẫn lộn xộn.

if (
    (
        x == y
        && a != b
        && p.isGood() 
        && (
            i + u == b
            || q >= a
        )
    )
    || k.isSomething()
    || m > n
) {
    doSomething();
}

1
Là sự thụt lề ngớ ngẩn và dấu ngoặc đơn / cấu trúc cú đúp có chủ ý hoặc một phần của phong cách?
Ed S.

Buồn cười. Tôi đã hỏi câu hỏi tương tự trên SO một tuần trước và nó đã bị đóng cửa. Vui mừng khi thấy câu hỏi này sống ở đâu đó!
Eric Belair

Câu trả lời:


6

Tạo các biến boolean cho mỗi bước nhỏ:

bool step1 = i + u == b || q >= a;
bool step2 = a != b && p.isGood() && group1;
bool step3 = group2 || k.isSomething() || m > n;
if (step3) { doSomething(); }

Tất nhiên điều này tương tự như câu trả lời của Lacrymology, ngoại trừ với các tên khác nhau cho mỗi bước.

Nếu bạn đặt tên step1, step2step3theo những cách có ý nghĩa khái niệm tốt, thì đây sẽ là điều dễ đọc nhất. p.isGood()k.isSomething()đôi khi có thể được gọi trong các tình huống không có trong mã gốc của bạn, vì vậy đây sẽ không phải là một lựa chọn nếu các chức năng đó đắt tiền hoặc nếu bạn đang chạy mã này trong một vòng lặp rất chặt chẽ.

Mặt khác, bạn không cần lo lắng về hiệu suất đạt được mà việc tạo các biến mới có thể phải chịu; một trình biên dịch tốt sẽ tối ưu hóa chúng ra.

Một ví dụ với phát hiện va chạm hình chữ nhật (mà bạn có thể sẽ không sử dụng do lần nhấn hiệu suất nói trên):

if((a.x + a.width >= b.x || b.x + b.width >= a.x)
 && (a.y + a.height >= b.y || b.y + b.width >= a.y)
)
{ collision(); }

Có thể trở thành:

bool horizMatch = a.x + a.width >= b.x || b.x + b.width >= a.x;
bool vertMatch = a.y + a.height >= b.y || b.y + b.width >= a.y;
if(horizMatch && vertMatch) { collision(); }

Ngoài ra, nếu bạn muốn để lại mã của mình, tôi nghĩ điều đó cũng hoàn toàn tốt. Tôi thành thật nghĩ rằng mã của bạn là khá dễ đọc. Rõ ràng là tôi không biết chính xác a b x y i u p k m nnó là gì , nhưng theo như cấu trúc, nó có vẻ tốt với tôi.


8

Tôi thường xác định lại mã của mình để trở nên mô đun hơn nếu các điều kiện của tôi trở nên phức tạp.


Tôi sẽ tránh khúc xạ trong tình huống này. Thật là ngớ ngẩn khi kiểm tra các hàm nhỏ như vậy một cách cô lập và việc các định nghĩa lơ lửng bên ngoài hàm làm cho mã ít rõ ràng hơn.
Rei Miyasaka

Nó phụ thuộc vào mức độ bạn tái cấu trúc. Tôi cho rằng điều đó làm cho mã của bạn trở nên rõ ràng hơn khi có các tên hàm có ý nghĩa thay vì một chuỗi các điều kiện trông giống như một cuốn sách giáo khoa đại số được đưa vào mã của bạn.
JohnFx

Mặc dù về mặt trực quan, bạn có các chức năng của mình lơ lửng bên ngoài và sẽ không rõ ràng chính xác chức năng đó làm gì. Đó là khoảnh khắc một hộp đen cho đến khi bạn cuộn lên. Trừ khi bạn ở một ngôn ngữ cho phép các chức năng trong các chức năng, tôi không nghĩ rằng nó sẽ rất thuận tiện cho người đọc hoặc cho người viết bất kể bạn khúc xạ tốt như thế nào. Và nếu bạn đang sử dụng ngôn ngữ cho phép các chức năng trong các chức năng, thì rất có thể cú pháp hầu như không khác với việc khai báo một ràng buộc hoặc biến thay thế, ví dụ let x = a > bhoặc let f a b = a > b.
Rei Miyasaka

Các biến sẽ hoạt động tốt như nhau. Tôi cũng sẽ xem xét việc tái cấu trúc.
JohnFx

À được rồi.
Rei Miyasaka

8

Tôi sẽ làm một cái gì đó như thế này, ở mức độ phức tạp này

bool doIt = x == y && a != b && p.isGood();
doIt &= ( i + u == b || q >= a);
doIt |= k.isSomething() || m > n;

if(doIt)
{
    doSomething();
}

nó xấu, nhưng nó có thể đọc được và tôi khá chắc chắn trình biên dịch sẽ biết cách cấu trúc lại nó.

Mặt khác, nếu tôi từng thấy mình trong tình huống viết một câu lệnh IF như vậy, tôi sẽ suy nghĩ lại về giải pháp, bởi vì tôi CERTAIN có cách làm đơn giản hơn, hoặc ít nhất là trừu tượng hóa một số điều kiện đó (ví dụ: có thể x == y && a != b && p.isGood()thực sự chỉ có ý nghĩa this->isPolygon()và tôi có thể thực hiện phương pháp đó;


4

Tôi đang bị ám ảnh bởi sự liên kết dọc theo thời gian, nhưng hình thức chung của tôi với các biểu thức đa dòng là ...

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6)
       )
    && (   (expr7 == expr8)
        || (expr9 == expra)
       )
   )
{
  blah;
}

Những điểm chính...

  • Các parens đóng thẳng hàng với các parens mở, như với các dấu ngoặc nhọn.
  • Các biểu thức phụ phù hợp trong một dòng nằm trong một dòng và được căn chỉnh theo chiều dọc ở bên trái. Khi nó giúp dễ đọc, các toán tử infix trong các phần đơn dòng đó cũng được căn chỉnh theo chiều dọc.
  • Các dấu ngoặc nhọn tự nhiên tạo ra các đường gần như trống, giúp nhóm trực quan mọi thứ.

Đôi khi, tôi sẽ định dạng +*một số nhà khai thác khác cũng thích điều này. Khá nhiều biểu thức phức tạp có dạng tổng sản phẩm hoặc sản phẩm tổng (có thể nói đến "tổng" và "sản phẩm" boolean) vì vậy có lẽ đủ phổ biến rằng một phong cách nhất quán cho nó là đáng giá.

Hãy cẩn thận với điều này, mặc dù. Việc tái cấu trúc (chuyển các phần của biểu thức thành hàm hoặc tính toán và lưu trữ các phần trung gian trong một biến) thường tốt hơn là sử dụng thụt lề để cố gắng làm cho biểu thức overcomplex dễ đọc hơn.

Nếu bạn thích xếp các parens cận của bạn ở phía bên tay phải, tôi không ghét nó, nhưng tôi đoán nó không quá tệ. Đã đi quá xa, bạn có nguy cơ rằng một sai lầm có thể để lại sự thụt lề sai lệch những gì các dấu ngoặc đơn làm, mặc dù.

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6))

    && (   (expr7 == expr8)
        || (expr9 == expra)))
{
  blah;
}

+1 Tôi thích kiểu dáng của bạn. Nó trả lời trực tiếp câu hỏi của tôi, nhưng tôi nghĩ Rei Miyasaka đóng đinh gốc rễ của vấn đề. Nếu tôi lười làm phương pháp của Rei, tôi sẽ sử dụng kiểu dáng của bạn.
JoJo

Wow điều này thực sự tốt đẹp.
Rei Miyasaka

1

http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html

Tôi đồng ý với câu trả lời của JohnFx cũng như câu trả lời của Lacrymology. Tôi sẽ xây dựng một loạt các chức năng (tốt nhất là tĩnh) để hoàn thành các mục tiêu nhỏ và sau đó xây dựng chúng theo cách thông minh.

Vì vậy, làm thế nào về một cái gì đó như thế này? Lưu ý, đây không phải là giải pháp hoàn hảo, nhưng nó hoạt động. Có nhiều cách để làm sạch điều này hơn nữa, nhưng cần thông tin cụ thể hơn. Lưu ý: mã này sẽ chạy nhanh như vậy, vì trình biên dịch là thông minh.

// Currently based on members or global vars
// (which is often a bad idea too)
function doSomethingCondirionally()
{
  if (k.isSomething() || m > n)
  {
    doSomething();
    return;
  }

  // Else ... 
  if (x != y) return;
  if (a == b) return;
  if (!p.isGood()) return;

  // Final, positive check
  if (i + u == b || q >= a)
  {
    doSomething();
  }
}

Nếu chỉ có một điểm thoát khỏi chức năng là thứ bạn đánh giá cao (ví dụ: nếu bạn đang sử dụng ngôn ngữ chức năng), đây có thể không phải là tùy chọn tốt nhất hoặc thậm chí là một điểm khả dụng. Điều đó nói rằng, yeah, đây là những gì tôi thường làm.
Rei Miyasaka

Điều gì xảy ra nếu đó là một ngôn ngữ được giải thích như Javascript?
JoJo

@ Rei Miyasaka, tôi đánh giá nó bao nhiêu tùy thuộc vào ngôn ngữ. Mặc dù tôi thích họ ngôn ngữ LISP, tôi không phải sử dụng ngôn ngữ này trong công việc. Nếu tôi phải xác định lại chức năng của người khác nhưng không chạm vào mã nào khác (thường là thực tế), thì tôi sẽ làm một cái gì đó như trên. Nếu tôi có thể viết / viết lại logic này từ đầu, thì cách tiếp cận của tôi sẽ khác, nhưng tôi không thể viết mã như vậy mà không có một ví dụ cụ thể về những gì tác giả đang cố gắng làm ở đây.
Công việc

1
@Rei Miyasaka, Người đó có thể là một thiên tài hoặc đầy rác rưởi. Tôi không biết tất cả mọi thứ, nhưng tôi sẽ tò mò muốn biết rằng người đó bảo vệ điểm thoát đơn. Có một số cuộc thảo luận về điều đó ở đây và trên SO, và ấn tượng tôi nhận được là cách tiếp cận này đã được các học giả trong thập niên 80 phổ biến, nhưng không còn quan trọng nữa, và trên thực tế có thể cản trở khả năng đọc. Tất nhiên, nếu bạn đang làm mọi thứ theo kiểu chức năng LINQ, thì vấn đề này thậm chí sẽ không xảy ra.
Công việc

2
@Job @Steve Tôi nghĩ rằng đó là một hướng dẫn quan trọng hơn trong các ngôn ngữ yêu cầu sự phân bổ rõ ràng. Rõ ràng không phải cho mọi chức năng, nhưng có lẽ đó là thói quen mà các lập trình viên mới bắt đầu được khuyến khích nắm giữ để tránh quên tài nguyên miễn phí.
Rei Miyasaka

1

Đối với những gì nó có giá trị, tôi đã ngạc nhiên khi thấy rằng ví dụ của bạn trông rất giống với các vị từ phức tạp tôi đã viết. Tôi đồng ý với những người khác rằng một vị từ phức tạp không phải là thứ tốt nhất cho khả năng duy trì hoặc khả năng đọc, nhưng đôi khi chúng xuất hiện.

Hãy để tôi nhấn mạnh rằng bạn đã làm phần này đúng: && a != b KHÔNG BAO GIỜ đặt trình kết nối logic ở cuối dòng, quá dễ để bỏ qua trực quan. Một nơi khác mà bạn KHÔNG BAO GIỜ đặt một toán tử ở cuối dòng là nối chuỗi, trong các ngôn ngữ với toán tử đó.

Làm cái này:

String a = b
   + "something"
   + c
   ;

Đừng làm điều này:

String a = b +
   "something" +
   c;

Bạn có bất kỳ logic hoặc nghiên cứu hỗ trợ khẳng định của bạn cho các kết nối logic, hoặc đó chỉ là sở thích của bạn được nêu là thực tế? Tôi đã nghe điều này rất nhiều ở nhiều nơi và chưa bao giờ hiểu nó (không giống như các điều kiện của yoda, có lý do [nếu hiểu sai] hợp lệ).
Caleb Huitt - cjhuitt

@Caleb - Toán học đã được sắp xếp theo kiểu đó trong nhiều thế kỷ. Khi đọc mã, chúng tôi tập trung vào phía bên trái của mỗi dòng. Một dòng bắt đầu bằng một toán tử rõ ràng là sự tiếp nối của dòng trước đó và không phải là một câu lệnh mới được thụt lề không chính xác.
kevin cline

Tôi cũng thích tiền tố của các toán tử toán học. Tôi nhận ra nó đến muộn trong sự nghiệp lập trình của mình :)
JoJo

0

Nếu điều kiện phức tạp như vậy, nó thường là một dấu hiệu cho thấy nó nên được chia thành nhiều phần. Có lẽ một mệnh đề có thể được gán cho một biến trung gian. Có lẽ một mệnh đề có thể được biến thành một phương thức trợ giúp. Tôi thường không thích có quá nhiều ands trong một dòng.


0

Bạn có thể chia mã thành nhiều câu lệnh, làm cho nó dễ hiểu. Nhưng một ninja thực sự sẽ làm một cái gì đó như thế này. :-)

if
(
    (
        x == y
    &&
        a != b
    &&
        p.isGood()
    &&
        (
            i + u == b
        ||
            q >= a
        )
    )
||
    k.isSomething()
||
    m > n
)
{
    doSomething();
}

5
Tôi là một fan hâm mộ của khoảng trắng, nhưng điều này được đệm quá mức với các dòng gần như trống cho sở thích của tôi.
Steve314
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.