Tôi nên sử dụng câu lệnh chuyển đổi hoặc dài nếu chuỗi khác?


36

Thông thường khi tôi nghe về câu lệnh chuyển đổi, nó sẽ tắt như một cách để thay thế lâu nếu ... chuỗi khác. Nhưng có vẻ như khi tôi sử dụng câu lệnh chuyển đổi, tôi đang viết thêm mã mà tôi sẽ chỉ viết nếu ... khác. Bạn cũng có các vấn đề khác như giữ tất cả các biến cho tất cả các cuộc gọi trong cùng một phạm vi .

Đây là một số mã đại diện cho luồng tôi thường viết ( nhờ diam )

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

if (which == 0) {
    comment = "You look so much better than usual.";
} else if (which == 1) {
    comment = "Your work is up to its usual standards.";
} else if (which == 2) {
    comment = "You're quite competent for so little experience.";
} else {
    comment = "Oops -- something is wrong with this code.";
}

Sau đó, họ muốn tôi thay thế bằng cái này:

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

switch (which) {
    case 0:  
             comment = "You look so much better than usual.";
    break;
    case 1:  
             comment = "Your work is up to its usual standards.";
    break;
    case 2:  
             comment = "You're quite competent for so little experience.";
    break;
    default: 
             comment = "Oops -- something is wrong with this code.";
}

Có vẻ như nhiều mã hơn trong một cú pháp khó xử hơn nhiều. Nhưng có thực sự có lợi thế khi sử dụng câu lệnh switch?


Ừ Vâng, đó chắc chắn là cồng kềnh hơn, nhưng chỉ trong gia đình C, bởi vì cú pháp của nó cho các báo cáo trường hợp là rất xấu.
Mason Wheeler


1
Bạn nên tránh cả hai bất cứ khi nào có thể. Sẽ tốt hơn nhiều khi tạo cấu trúc dữ liệu và thực hiện tra cứu, ngay cả khi mục tiêu tra cứu là một hàm hoặc lớp.
kevin cline

chuyển đổi nhanh hơn, ít nhất là trong .net. Tôi không biết về Java.
Knerd

Có thể được cấu trúc lại thành một từ điển các trường hợp và phương pháp và một "nếu"
Pavel Yermalovich

Câu trả lời:


57

Đối với tình huống đặc biệt này, dường như cả hai ifcaseđều là những lựa chọn tồi. Tôi sẽ sử dụng một mảng đơn giản:

String comments[] = {
    "You look so much better than usual.",
    "Your work is up to its usual standards.",
    "You're quite competent for so little experience."
};

String comment = comments[(int)(Math.random() * 3)];

Là một lưu ý phụ, bạn thường nên tính hệ số nhân dựa trên kích thước của mảng thay vì mã hóa cứng 3.

Như khi bạn sẽ sử dụng một trường hợp / switch, sự khác biệt từ một thác của ifbáo cáo (hoặc ít nhất là một sự khác biệt lớn) là switchcó thể bán tự động tối ưu hóa dựa trên số lượng và mật độ của các giá trị, trong khi một chuỗi các ifcâu lá trình biên dịch với rất ít sự lựa chọn nhưng để tạo mã như bạn đã viết nó, kiểm tra giá trị này đến giá trị khác cho đến khi tìm thấy sự trùng khớp. Chỉ với ba trường hợp thực tế, đó không phải là vấn đề đáng lo ngại, nhưng với một số lượng đủ, nó có thể / có thể là đáng kể.


Ví dụ đó chỉ là một ví dụ BTW. Mặc dù vậy, không biết rằng trình biên dịch có thể tối ưu hóa như vậy
TheLQ

6
@JBRWilkinson. Trong trường hợp này, giá trị ngoài giới hạn chỉ có thể thông qua lỗi trình biên dịch, điều mà tôi không thích dành nhiều thời gian cho (một lỗi trong mã của tôi để kiểm tra kết quả có khả năng giống như trong mã tạo). Trong trường hợp giá trị ngoài giới hạn là mối quan tâm thực sự (ví dụ: chỉ mục được nhận từ mã khác) Tôi chỉ cần kiểm tra giới hạn trước và chỉ sử dụng nó làm chỉ mục sau khi kiểm tra.
Jerry Coffin

4
Tôi nghĩ rằng câu trả lời này rất cụ thể về ví dụ, trong khi câu hỏi chung chung hơn thế ...
Khelben

3
@Khelben: có vẻ như tôi không buồn đọc toàn bộ câu trả lời. Đoạn cuối đề cập đến các vấn đề rộng lớn hơn. Có một vấn đề mặc dù: Tôi thấy rất ít tình huống mà tôi xem xét một trong hai một casetuyên bố hoặc một chuỗi các ifbáo cáo phù hợp. Hầu hết thời gian, chúng là một sự thay thế (tầm thường) cho một số loại bản đồ / mảng của sự vật, và tốt hơn hết là bạn nên sử dụng trực tiếp một trong những thứ sau.
Jerry Coffin

1
@Titou: trừ khi trình biên dịch hoàn toàn dũng cảm, mảng sẽ được xây dựng một lần vào thời gian biên dịch và sau đó bạn chỉ sử dụng cấu trúc tĩnh. Ví dụ: nếu bạn đang làm điều này trong C hoặc C ++, bạn sẽ muốn tạo một static constmảng đó, để đảm bảo rằng nó luôn tồn tại (nhưng không có ngôn ngữ nào được đưa ra trong câu hỏi, vì vậy tôi đã cố gắng không đưa ra một câu trả lời ).
Jerry Coffin

23

Vấn đề với if...else if...chuỗi là khi tôi đọc nó, tôi phải xem xét từng ifđiều kiện để hiểu chương trình đang làm gì. Ví dụ: bạn có thể có một cái gì đó như thế này:

if (a == 1) {
    // stuff
} else if (a == 2) {
    // stuff
} else if (a == 3) {
    // stuff
} else if (b == 1) {
    // stuff
} else if (b == 2) {
    // stuff
}

(rõ ràng, đối với một số lượng nhỏ các tuyên bố như thế này, nó không quá tệ)

Tôi không có cách nào để biết rằng bạn đã thay đổi nửa điều kiện mà không đọc từng câu lệnh. Tuy nhiên, vì chỉ switchgiới hạn bạn ở một biến điều kiện duy nhất, tôi có thể thấy trong nháy mắt những gì đang xảy ra.

Tuy nhiên, vào cuối ngày, tôi không thích switchhoặc một chuỗi if...else if. Thường thì một giải pháp tốt hơn là một số loại bảng nhảy hoặc từ điển cho các trường hợp như trong câu hỏi ban đầu hoặc đa hình (nếu ngôn ngữ của bạn hỗ trợ điều đó). Tất nhiên không phải lúc nào cũng có thể, nhưng tôi muốn tìm một giải pháp tránh switchbước đầu tiên ...


4
Tính đa hình có nhược điểm là phân đoạn mã, làm cho nó khó hiểu hơn so với ở một vị trí. Do đó bạn có thể do dự một chút trước khi thay đổi điều đó.

Tại sao một bảng nhảy / từ điển tốt hơn một công tắc?
Titou

14
switch (which) {
  case 0: comment = "String 1"; break;
  case 1: comment = "String 2"; break;
  case 2: comment = "String 3"; break;
  default: comment = "Oops"; break;
}

Cách viết trên của loại trường hợp chuyển đổi này là khá phổ biến. Lý do tại sao bạn cảm thấy trường hợp chuyển đổi nếu cồng kềnh là vì cơ thể bạn chỉ có một dòng và với trường hợp chuyển đổi, bạn cũng cần câu lệnh break. Vì vậy, trường hợp chuyển đổi có kích thước cơ thể gấp đôi nếu khác. Với mã quan trọng hơn, câu lệnh break sẽ không thêm nhiều vào phần thân. Đối với thân dòng đơn, việc viết mã trong cùng dòng với câu lệnh tình huống là một cách phổ biến.

Như những người khác đã đề cập, trường hợp chuyển đổi làm cho ý định rõ ràng hơn, bạn muốn đưa ra quyết định dựa trên giá trị của một biến / biểu thức duy nhất. Nhận xét của tôi hoàn toàn từ quan điểm dễ đọc và không dựa trên hiệu suất.


1
Nếu bạn đặt công tắc trong một phương thức và có từng trường returnhợp chuỗi thích hợp, bạn có thể loại bỏ các breakcâu lệnh.
Robert Harvey

Giải pháp của bạn cũng là nhanh nhất.
Titou

8

Trong trường hợp này, câu lệnh chuyển đổi phù hợp rõ ràng hơn với mục đích của mã: chọn một hành động để thực hiện dựa trên một giá trị duy nhất.

Mặt khác, các câu lệnh if khó đọc hơn nhiều - bạn phải xem tất cả chúng để chắc chắn điều gì đang xảy ra. Đối với tôi, nó ít mã hơn (ngay cả khi số lượng ký tự thể cao hơn một chút) vì ít phân tích về mặt tinh thần.


8

Tôi đồng ý với Jerry rằng một chuỗi các chuỗi tốt hơn cho trường hợp cụ thể này, nhưng nói chung, tốt hơn là sử dụng câu lệnh chuyển đổi / trường hợp hơn là một chuỗi các chuỗi khác. Nó dễ đọc hơn và đôi khi trình biên dịch có thể thực hiện tốt hơn việc tối ưu hóa theo cách đó, nhưng cũng có một lợi ích khác: đó là một cách dễ dàng hơn để gỡ lỗi.

Khi bạn nhấn công tắc đó, bạn chỉ phải bước một lần để kết thúc ở nhánh bên phải, thay vì cẩn thận bước qua vài câu lệnh một lần, và có thể nhấn phím quá nhanh và bước qua nó và thiếu một cái gì đó và có làm lại từ đầu.


3

Tôi thích chuyển đổi trong các trường hợp đó, nó phù hợp hơn với điểm của mã, thực thi một câu lệnh khác nhau cho mỗi giá trị đầu vào khác nhau. Các if..elsehành động giống như một "mẹo" để đạt được hiệu quả tương tự.

switch báo cáo cũng sạch hơn, thật dễ dàng để có một lỗi đánh máy trong tất cả những câu lệnh ==

Ngoài ra, đối với các khối lớn trong C, chuyển đổi nhanh hơn.

else..ifcó thể phù hợp hơn khi bạn có thứ gì đó như phạm vi (từ 1 đến 100, làm điều này, từ 100 đến 200 làm điều đó) hoặc trong C, khi bạn cố gắng thực hiện chuyển đổi với các thành phần như chuỗi (có thể bằng các ngôn ngữ khác). Mà giống nhau.

Tôi có xu hướng sử dụng nhiều công tắc khi tôi lập trình trong C.


2

Chọn một cái gì đó hiệu quả, ngắn gọn, và sau đó ghi lại không chỉ những gì bạn đã làm, mà tại sao.

Mã có thể được xem lại, và không phải lúc nào cũng bởi tác giả ban đầu của nó.

Đôi khi bạn có thể cố tình chọn một triển khai trên một triển khai khác bạn đang nghĩ về mã không tồn tại.


2

Tôi thường không thích một trong hai cách tiếp cận. Chuyển đổi dài hoặc nếu các câu lệnh chỉ xin được tái cấu trúc thành một trừu tượng hướng đối tượng (tuy nhiên ví dụ của bạn tôi sẽ phân loại là ngắn, không dài).

Cá nhân tôi sẽ bọc loại mã đó vào một phương thức trợ giúp riêng.

private string GetInsult()
{
    int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

    switch (which) {
        case 0: return "You look so much better than usual.";
        case 1: return "Your work is up to its usual standards.";
        case 2: return "You're quite competent for so little experience.";
        default: return "Oops -- something is wrong with this code.";
    }
}

public void Foo()
{
    string comment = GetInsult();
    Print(comment);
}

Đặt công tắc trong một phương thức riêng biệt cho phép bạn đặt các câu lệnh trả lại trực tiếp bên trong câu lệnh chuyển đổi (ít nhất là trong c #), loại bỏ sự cần thiết của các câu lệnh ngắt, làm cho mã dễ đọc hơn nhiều.

Và điều này là imho đẹp hơn nhiều so với if / other if / other if tiếp cận.


3
Cá nhân tôi ghét cách "đặt nó vào một phương pháp khác bởi vì nó trông xấu xí" để giải quyết mọi việc. Clutter danh sách phương pháp và trông IMHO tồi tệ hơn. Tôi sẽ chỉ làm điều này nếu A) mã được sao chép ở đâu đó hoặc B) Có thể hữu ích ở nơi khác
TheLQ

1
danh sách phương pháp nào? và tại sao danh sách phương thức của bạn bị lộn xộn tồi tệ hơn mã của bạn bị lộn xộn? Tôi nghĩ rằng chúng tôi đã vượt qua "giữ mọi thứ trong một phương thức duy nhất để bạn có thể thấy mọi thứ cùng một lúc"
sara

@TheLQ Tôi đồng ý với bạn nói chung, nhưng trong trường hợp này, "bình luận =" thực sự được đề xuất bởi đề xuất của Pete.
Titou

0

Trong python, không có câu lệnh switch, bởi vì if / elif / other là tốt:

a = 5

if a==1:
    print "do this"
elif a == 2:
    print "do that"
elif a == 3:
    print "do the other"
elif 3 < a < 9:
    print "do more"
elif 9 <= a < 15:
    print "do nothing"
else:
    print "say sorry"

Đơn giản phải không?


Elifchỉ là một câu lệnh if có một vài chữ cái bị thiếu. Nó chắc chắn giống như một iftuyên bố hơn là một tuyên bố chuyển đổi. Việc Python KHÔNG có công tắc khiến một người ghét chúng (như tôi) nghĩ rằng chúng không đơn độc.
Dan Rosenstark

Định dạng Python hoạt động ở stackoverflow nhưng không phải ở lập trình viên.stackexchange.com :(
Christopher Mahan

Bạn nên cảnh báo họ tại metatrừ khi đó là một chủ đề được biết đến. Cảm ơn đã cho tôi một nhân chứng.
Dan Rosenstark

ya, phát hiện ra họ có nó đang diễn ra tại meta.programmers.stackexchange.com/questions/308/ Kẻ
Christopher Mahan

1
@Yar, Nhắc tôi về những ngày quản trị wikipedia của tôi ... Oh Joy. (tôi hoàn toàn lạc đề chưa?)
Christopher Mahan

0

Một trong những điều khiến phong cách C / C # switchđặc biệt khó chịu là sự khăng khăng về casegiá trị là nghĩa đen. Một điều thú vị về VB / VB.NET là select/casecho phép mỗi trường hợp là bất kỳ biểu thức boolean nào. Đó là thuận tiện. Trong chừng mực, một loạt các biểu thức boolean loại trừ lẫn nhau thường rất hữu ích, một loạt if / other ifs linh hoạt hơn, chưa kể đến việc gõ và đọc hiệu quả hơn.

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.