it-swarm-vi.com

Toán tử ternary coi có hại?

Ví dụ, bạn có thích lớp lót này không

int median(int a, int b, int c) {
    return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
}

hoặc một giải pháp if/other liên quan đến nhiều câu lệnh return?

Khi nào ?: thích hợp, và khi nào thì không? Nó nên được dạy cho hoặc ẩn từ người mới bắt đầu?

82
fredoverflow

Là người điều hành ternary ác?

Không, đó là một phước lành.

Khi nào ?: Thích hợp?

Khi nó đơn giản đến mức bạn không muốn lãng phí nhiều dòng cho.

và khi nào thì không?

Khi khả năng đọc và độ rõ ràng của mã bị ảnh hưởng và có khả năng xảy ra lỗi thông qua sự chú ý không đủ, chẳng hạn, với nhiều toán tử bị xiềng xích, giống như trong ví dụ của bạn.


Kiểm tra litmus là khi bạn bắt đầu nghi ngờ mã của mình có thể dễ dàng đọc và duy trì trong thời gian dài. Vậy thì đừng làm.

236
user8685

Tôi nghĩ rằng toán tử ternary chưa được kiểm tra (nghĩa là, một câu lệnh chỉ được sử dụng một lần) là tốt, nhưng nếu bạn lồng nhiều hơn một, nó sẽ hơi khó đọc.

50
mipadi

Khi nào ?: Thích hợp

  • Khi nó làm cho mã của bạn ngắn gọn và dễ đọc hơn.

và khi nào thì không?

  • Khi nó làm cho mã của bạn không thể đọc được.
  • Nếu bạn đang làm điều đó chỉ để làm hài lòng một công cụ tái cấu trúc như ReSharper chứ không phải người phải duy trì mã

Nếu bạn có bất kỳ lệnh gọi logic hoặc hàm nào bên trong biểu thức ternary, bạn sẽ thấy thật kinh khủng khi nhìn vào.

24
realworldcoder

Một điểm khác biệt mà (tôi nghĩ) không ai đã chỉ ra là nếu người khác không thể trả về một giá trị, trong khi toán tử ternary có thể.

Đến từ F #, đôi khi tôi thích sử dụng toán tử ternary để bắt chước khớp mẫu.

match val with
| A -> 1
| B -> 3
| _ -> 0

đấu với

return val == A ? 1 : 
       val == B ? 3 : 
       0;
23
Benjol

Một ví dụ (IMHO) về việc sử dụng hợp lệ:

printf("Success in %d %s\n", nr_of_tries, (nr_of_tries == 1 ? "try" : "tries"));

Điều này dẫn đến mã dễ đọc hơn là có 2 câu lệnh in riêng biệt. Các ví dụ lồng nhau phụ thuộc: (có thể hiểu được? Có: không)

13
Henno Brandsma

Tuyệt đối không ác. Trên thực tế, đó là thuần túy , và nếu không thì không.

Trong các ngôn ngữ chức năng như Haskell, F #, ML, v.v., đó là những câu lệnh if-then-other được coi là xấu xa.

Lý do cho điều này là bất kỳ "hành động" nào như một câu lệnh if-then-other bắt buộc đều yêu cầu bạn tách một khai báo biến khỏi định nghĩa của nó và giới thiệu state cho hàm của bạn.

Chẳng hạn, trong đoạn mã sau:

const var x = n % 3 == 1
    ? Parity.Even
    : Parity.Odd;

vs.

Parity x;
if (n % 3 == 1)
    x = Parity.Even;
else
    x = Parity.Odd;

Cái đầu tiên có hai ưu điểm ngoài việc ngắn hơn:

  1. x là một hằng số, và do đó cung cấp ít cơ hội giới thiệu các lỗi hơn và có khả năng có thể được tối ưu hóa theo cách thứ hai không bao giờ có thể xảy ra.
  2. Loại được làm rõ bởi biểu thức, vì vậy trình biên dịch có thể dễ dàng suy ra rằng x cần phải là loại Parity.

Một cách khó hiểu, trong các ngôn ngữ chức năng, toán tử ternary thường được gọi là if-then-other. Trong Haskell, bạn có thể nói x = if n mod 3 == 1 then Odd else Even.

7
Rei Miyasaka

Biểu hiện đặc biệt đó khiến mắt tôi đau; Tôi đã loại bỏ bất kỳ nhà phát triển nào trong nhóm của tôi đã sử dụng nó bởi vì nó không thể nhầm lẫn được.

Toán tử ternary không xấu khi được sử dụng tốt. Họ thậm chí không phải là một dòng duy nhất; Một cái dài được định dạng tốt có thể rất rõ ràng và dễ hiểu:

return
      ( 'a' == $s ) ? 1
    : ( 'b' == $s ) ? 2
    : ( 'c' == $s ) ? 3
    :                 4;

Tôi thích điều đó tốt hơn chuỗi tương đương if/then/other:

if ( 'a' == $s ) {
    $retval = 1;
}
elsif ( 'b' == $s ) {
    $retval = 2;
}
elsif ( 'c' == $s ) {
    $retval = 3;
}
else {
    $retval = 4;
}

return $retval;

Tôi sẽ định dạng lại chúng thành:

if    ( 'a' == $s ) { $retval = 1; }
elsif ( 'b' == $s ) { $retval = 2; }
elsif ( 'c' == $s ) { $retval = 3; }
else                { $retval = 4; }

return $retval;

nếu các điều kiện và bài tập cho phép căn chỉnh dễ dàng. Tuy nhiên, tôi thích phiên bản ternary hơn vì nó ngắn hơn và không có nhiều tiếng ồn xung quanh các điều kiện và bài tập.

7
the Tin Man

ReSharper trong VS.NET đôi khi đề nghị thay thế if...else với ?: nhà điều hành.

Dường như ReSharper chỉ đề xuất nếu các điều kiện/khối nằm dưới một mức độ phức tạp nhất định, nếu không, nó sẽ dính với if...else.

3
Uwe Keim

Đây là một ví dụ về khi nó is evil:

oldValue = newValue >= 0 ? newValue : oldValue;

Thật khó hiểu và lãng phí. Trình biên dịch có thể tối ưu hóa biểu thức thứ hai (oldValue = oldValue), nhưng tại sao bộ mã hóa lại làm điều này ngay từ đầu?

Một doozy khác:

thingie = otherThingie != null ? otherThingie : null;

Một số người không có nghĩa là lập trình viên ...

Greg nói rằng tương đương nếu tuyên bố là 'ồn ào'. Đó là nếu bạn viết nó ồn ào. Nhưng điều tương tự nếu có thể được viết là:

if ('a' == $s) return 1;
if ('b' == $s) return 2;
if ('c' == $s) return 3;
return 4;

Mà không ồn ào hơn chim nhạn. Tôi tự hỏi nếu cắt ngắn ternary; làm tất cả các biểu thức được đánh giá?

2
hapybrian

Khác xa với cái ác, người điều khiển chim nhạn là một ơn trời.

  • Nó hữu ích nhất khi bạn muốn đưa ra quyết định trong biểu thức lồng nha. Ví dụ cổ điển là một lệnh gọi hàm:

    printf("I see %d evil construct%s in this program\n", n, n == 1 ? "" : "s");
    
  • Trong ví dụ cụ thể của bạn, ternary gần như vô cớ, vì nó là biểu thức cấp cao nhất dưới một return. Bạn có thể nâng điều kiện ra mức câu lệnh mà không cần sao chép bất kỳ thứ gì ngoài từ khóa return.

N.B. Không có gì sẽ làm cho thuật toán cụ thể đó cho trung bình dễ đọc.

2
Norman Ramsey
  1. Theo kinh nghiệm của tôi, tôi đã tìm thấy một mối tương quan cao giữa việc lập trình viên sử dụng toán tử ternary và khả năng toàn bộ cơ sở mã của họ khó đọc, theo dõi và duy trì (nếu không hoàn toàn không có giấy tờ). Nếu một lập trình viên quan tâm đến việc lưu một vài dòng 1-2 ký tự hơn là ai đó có thể hiểu được mã của anh ta, thì bất kỳ sự nhầm lẫn nhỏ nào khi hiểu một tuyên bố tạm thời thường là phần nổi của tảng băng.

  2. Toán tử ternary thu hút số ma thuật như s ** t thu hút ruồi.

Nếu tôi đang tìm kiếm một thư viện Nguồn mở để giải quyết một vấn đề cụ thể và tôi thấy mã như các toán tử tạm thời của người đăng bài ban đầu trong một ứng cử viên cho thư viện nói trên, tiếng chuông cảnh báo sẽ bắt đầu vang lên trong đầu tôi và tôi bắt đầu xem xét tiếp tục cho một số dự án khác để vay từ.

2
user8865

Tà ác? Hãy nhìn xem, họ chỉ khác nhau.

if là một tuyên bố. (test ? a : b) là một biểu hiện. Chúng không giống nhau.

Biểu thức tồn tại để thể hiện giá trị. Báo cáo tồn tại để thực hiện hành động. Biểu thức có thể xuất hiện bên trong các câu lệnh, nhưng không phải ngược lại. Vì vậy, bạn có thể sử dụng các biểu thức ternary trong các biểu thức khác, như cho các thuật ngữ trong tổng kết hoặc cho các đối số cho một phương thức, v.v. Bạn không phải, nhưng bạn có thể nếu bạn muốn. Không có gì sai với điều đó. Một số người có thể nói đó là xấu xa, nhưng đó là ý kiến ​​của họ.

Một giá trị của biểu thức ternary là nó làm cho bạn xử lý cả trường hợp đúng và sai. if báo cáo không.

Nếu bạn lo lắng về khả năng đọc, bạn có thể định dạng chúng dễ đọc.

Bằng cách nào đó "cái ác" len lỏi vào từ vựng lập trình. Tôi muốn biết ai là người đầu tiên bỏ nó. (Trên thực tế, tôi có một nghi phạm - anh ta ở MIT.) Tôi thà rằng chúng ta có những lý do khách quan cho những đánh giá giá trị trong lĩnh vực này, không chỉ là sở thích và cách gọi tên của mọi người.

2
Mike Dunlavey

Điều này có thể được định dạng lại để trông đẹp mắt như sự kết hợp if/other:

int median(int a, int b, int c)
{
    return
        (a<b)
        ?
            (b<c)
            ? b
            :
                (a<c)
                ? c
                : a
        :
            (a<c)
            ? a
            :
                (b<c)
                ? c
                : b;
}

Nhưng vấn đề là tôi không thực sự chắc chắn nếu tôi có quyền thụt lề để đại diện cho những gì sẽ thực sự xảy ra. :-)

2
Zan Lynx

Tôi có thể nói không? Tôi không thể quản lý để tìm ứng dụng cụ thể này của hoạt động ternary evil:

  1. hoạt động mà nó thực hiện khá tầm thường, và một khi bạn đã thực hiện nó một lần thì khó có thể một số lỗi sẽ xuất hiện;
  2. những gì nó làm được nêu rõ trong tên hàm;
  3. lấy> 1 dòng cho một cái gì đó quá rõ ràng và rõ ràng sẽ không được cải thiện trong tương lai (trừ khi thuật toán trung bình ma thuật đã không bị phát hiện cho đến bây giờ).

Xin thương xót, danh tiếng của tôi khá đáng thương rồi.

1
cbrandolino

Bất cứ điều gì làm cho mã của bạn xấu hơn là xấu xa.

Nếu bạn sử dụng ternary để làm cho mã của bạn sạch hơn thì chắc chắn sử dụng nó. Đôi khi, giống như php, thật tuyệt khi thực hiện thay thế nội tuyến, ví dụ:.

"Hello ".($Male?"Mr":"Ms")." $Name

Điều đó tiết kiệm một vài dòng và khá rõ ràng, nhưng ví dụ của bạn cần ít nhất là định dạng tốt hơn để rõ ràng và ternary không thực sự tốt cho đa dòng, sau đó bạn cũng có thể sử dụng if/other.

1
user11134

Chiến thắng lớn nhất: Cho thấy rằng có một mục tiêu hành động duy nhất.

if ( $is_whatever )
    $foo = 'A';
else
    $foo = 'B';

Có hai đường dẫn mã mà bạn có thể theo dõi và người đọc phải đọc kỹ để xem hai biến nào đang được đặt. Trong trường hợp này, đó chỉ là một biến, nhưng người đọc có nhiều thứ để đọc hơn để tìm ra điều đó. Rốt cuộc, nó có thể là thế này:

if ( $is_whatever )
    $foo = 'A';
else
    $bar = 'B';

Với toán tử ternary, rõ ràng chỉ có một biến được đặt.

$foo = $is_whatever ? 'A' : 'B';

Ở cấp độ thấp nhất, đó là nguyên tắc DRY (Không lặp lại chính mình) ở mức cơ bản nhất. Nếu bạn có thể chỉ định $foo chỉ một lần, làm như vậy.

1
Andy Lester

Nó có một nơi. Tôi đã làm việc tại nhiều công ty, nơi trình độ kỹ năng của các nhà phát triển trải dài từ khủng khiếp đến phù thủy. Vì mã phải được duy trì và tôi sẽ không ở đó mãi mãi, tôi cố gắng viết những thứ để nó trông giống như ở đó (mà không cần nhìn vào các bình luận với tên viết tắt của tôi, điều đó cực kỳ hiếm khi bạn có thể nhìn vào mã tôi đã làm việc để xem nơi tôi đã thực hiện các thay đổi) và rằng ai đó có ít kỹ năng hơn bản thân tôi có thể duy trì nó.

Trong khi toán tử ternary trông nitziffic và tuyệt vời, kinh nghiệm của tôi là dòng mã sẽ không thể duy trì. Tại nhà tuyển dụng hiện tại của tôi, chúng tôi có các sản phẩm đã được vận chuyển gần 20 năm. Tôi sẽ không sử dụng ví dụ đó ở bất cứ đâu.

1
Tangurena

Tôi không nghĩ rằng người điều hành chim nhạn là xấu xa.

Đây là một gotcha mà tôi đã bối rối, mặc dù. Tôi là một lập trình viên C cho nhiều người (10+) và vào cuối những năm 1990, tôi chuyển sang lập trình ứng dụng dựa trên web. Là một lập trình viên web, tôi sớm chạy qua PHP, cũng có một toán tử ternary. Tôi đã gặp lỗi trong chương trình PHP mà cuối cùng tôi đã truy ra một dòng của mã với toán tử ternary lồng nhau. Hóa ra toán tử PHP ternary liên kết từ trái sang phải nhưng toán tử ternary C (mà tôi đã sử dụng) liên kết từ phải sang trái.

1
leed25d

Khi nào thì thích hợp, và khi nào thì không?

Tôi nghĩ rằng khi phát triển cho một nhóm người đồng nhất, không có vấn đề gì, nhưng khi bạn phải đối phó với những người xử lý các cấp độ khác nhau, loại onelin này chỉ đưa ra một mức độ phức tạp hơn cho mã. Vì vậy, chính sách của tôi về vấn đề này là: mã rõ ràng và không giải thích thay vì mã ngắn và giải thích 123123 lần.

Nó nên được dạy cho hoặc ẩn từ người mới bắt đầu?

Tôi không nên được dạy cho người mới bắt đầu, thay vào đó họ thích tìm hiểu nó khi có nhu cầu, vì vậy nó sẽ chỉ được sử dụng khi cần và không phải mỗi khi bạn cần.

0
guiman

Một cửa hàng thường xuyên viết các phương thức 600-1200 dòng không nên cho tôi biết rằng một ternary là "khó hiểu". Bất kỳ cửa hàng nào thường xuyên cho phép năm điều kiện để đánh giá một nhánh mã không nên cho tôi biết rằng các điều kiện được tóm tắt cụ thể trong một ternary là "khó đọc".

0
Axeman

IMO, bản thân toán tử không phải là xấu, nhưng cú pháp được sử dụng cho nó trong C (và C++) là quá ngắn gọn. IMO, ALGOL 60 đã làm điều đó tốt hơn, vì vậy một cái gì đó như thế này:

A = x == y ? B : C;

sẽ trông giống như thế này (nhưng nói chung theo cú pháp giống như C):

A = if (x==y) B else C;

Ngay cả với điều đó, việc lồng quá sâu có thể dẫn đến các vấn đề về khả năng đọc, nhưng ít nhất A) bất kỳ ai thực hiện lập trình đều có thể tìm ra một cách đơn giản và B) những người hiểu nó có thể xử lý việc làm tổ sâu hơn đáng kể khá dễ dàng. OTOH, tôi cũng lưu ý rằng trong LISP (ví dụ), một cond khá giống với một câu lệnh ternary - không phải là một tập hợp các câu lệnh, mà là một biểu thức duy nhất mang lại một giá trị (sau đó, hầu hết LISP là như thế ...)

0
Jerry Coffin

Khi nào ?: Thích hợp và khi nào thì không?

  • Nếu bạn không đạt được hiệu suất, hãy sử dụng nó; nó ảnh hưởng đến khả năng đọc mã của bạn.
  • Sử dụng một lần và don lồng nó.
  • Nó khó hơn để gỡ lỗi.

Nên dạy hay ẩn từ người mới bắt đầu?

Không quan trọng, nhưng nó không nên bị che giấu một cách có chủ ý vì nó không quá phức tạp đối với một người mới bắt đầu học tiếng Anh.

0
Amir Rezaei

Nếu ... thì ... khác có xu hướng nhấn mạnh điều kiện và do đó nhấn mạnh đến các hoạt động được thực hiện có điều kiện.

toán tử ternary thì ngược lại, nó có xu hướng che giấu điều kiện và có ích khi hoạt động được thực hiện quan trọng hơn chính điều kiện.

Có một vấn đề nhỏ về kỹ thuật, trong một số ngôn ngữ, chúng không hoàn toàn thay thế nhau do một là một tuyên bố và một biểu thức, ví dụ: consts khởi tạo có điều kiện trong C++

0
jk.