Haruskah saya menggunakan pernyataan switch atau lama jika ... rantai lain?

36

Seringkali ketika saya mendengar tentang pernyataan switch, itu menunda sebagai cara untuk mengganti panjang jika ... rantai lain. Tapi sepertinya ketika saya menggunakan pernyataan switch saya menulis lebih banyak kode yang saya hanya akan menulis jika ... lain. Anda juga memiliki masalah lain seperti menjaga semua variabel untuk semua panggilan dalam cakupan yang sama .

Inilah beberapa kode yang mewakili aliran yang biasanya saya tulis ( terima kasih untuk 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.";
}

Kemudian mereka ingin saya menggantinya dengan ini:

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

Sepertinya lebih banyak kode dalam sintaks yang jauh lebih canggung. Tetapi apakah benar-benar ada keuntungan menggunakan pernyataan switch?

TheLQ
sumber
Ugh. Ya, itu tentu saja bulkier, tetapi hanya di keluarga C, karena sintaksisnya untuk pernyataan kasus sangat jelek.
Mason Wheeler
1
Anda harus menghindari keduanya jika memungkinkan. Jauh lebih baik untuk membuat struktur data dan melakukan pencarian, bahkan jika target pencarian adalah fungsi atau kelas.
kevin cline
sakelar lebih cepat, setidaknya dalam .net. Saya tidak tahu tentang Java.
Knerd
Dapat di refactored ke kamus case dan metode dan satu "jika"
Pavel Yermalovich

Jawaban:

57

Untuk situasi tertentu ini, tampaknya bagi saya bahwa kedua ifdan caseadalah pilihan yang buruk. Saya akan menggunakan array sederhana:

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

Sebagai catatan, Anda biasanya harus menghitung pengali berdasarkan ukuran array daripada hard-coding 3.

Seperti ketika Anda akan menggunakan case / switch, perbedaan dari kaskade ifpernyataan (atau setidaknya satu perbedaan utama) adalah yang switchdapat mengoptimalkan secara semi-otomatis berdasarkan jumlah dan kepadatan nilai, sedangkan kaskade ifpernyataan meninggalkan kompilator dengan sedikit pilihan selain menghasilkan kode seperti yang telah Anda tulis, menguji satu demi satu nilai hingga menemukan kecocokan. Dengan hanya tiga kasus nyata, itu hampir tidak menjadi perhatian, tetapi dengan jumlah yang cukup dapat / bisa signifikan.

Jerry Coffin
sumber
Contoh itu hanya contoh BTW. Tidak tahu bahwa kompiler dapat mengoptimalkan seperti itu
TheLQ
6
@ JBRWilkinson. Dalam hal ini, nilai di luar batas hanya dimungkinkan melalui bug kompiler, yang saya enggan menghabiskan banyak waktu (bug dalam kode saya untuk menguji hasilnya hampir sama mungkin dengan yang ada di kode penghasil). Dalam situasi di mana nilai di luar batas adalah masalah nyata (misalnya, indeks diterima dari kode lain) saya baru saja memeriksa batas terlebih dahulu, dan menggunakannya sebagai indeks hanya setelah pemeriksaan.
Jerry Coffin
4
Saya pikir jawaban ini sangat spesifik tentang contohnya, sementara pertanyaannya lebih umum dari itu ...
Khelben
3
@Helben: Kedengarannya bagi saya seperti Anda tidak repot-repot membaca seluruh jawaban. Paragraf terakhir membahas masalah yang lebih luas. Ada masalah meskipun: Saya menemukan sangat sedikit situasi di mana saya anggap baik suatu casepernyataan atau riam ifpernyataan yang cocok. Sebagian besar waktu, mereka adalah pengganti (biasa-biasa saja) untuk semacam peta / jenis array hal, dan Anda lebih baik menggunakan salah satu yang terakhir secara langsung.
Jerry Coffin
1
@Titou: kecuali kompiler benar-benar braindead, array akan dibangun sekali pada waktu kompilasi, dan setelah itu Anda hanya menggunakan struktur statis. Misalnya, jika Anda melakukan ini dalam C atau C ++, Anda ingin membuat static constarray itu, untuk memastikan bahwa itu selalu ada (tapi tidak ada bahasa yang diberikan dalam pertanyaan, jadi saya mencoba untuk tidak mengasumsikan satu dalam jawaban baik ).
Jerry Coffin
23

Masalah dengan if...else if...rantai adalah ketika saya datang untuk membacanya, saya harus melihat setiap ifkondisi untuk memahami apa yang sedang dilakukan program. Misalnya, Anda mungkin memiliki sesuatu seperti ini:

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

(jelas, untuk sejumlah kecil pernyataan seperti ini, itu tidak terlalu buruk)

Saya tidak memiliki cara untuk mengetahui bahwa Anda mengubah variabel kondisi setengah jalan tanpa membaca setiap pernyataan. Namun, karena switchmembatasi Anda hanya untuk variabel kondisi tunggal, saya dapat melihat sekilas apa yang terjadi.

Pada akhirnya, saya lebih suka tidak switchsatu pun atau rantai if...else if. Seringkali solusi yang lebih baik adalah semacam jump table atau kamus untuk kasus-kasus seperti dalam pertanyaan awal, atau polimorfisme (jika bahasa Anda mendukungnya). Tentu saja tidak selalu memungkinkan, tetapi saya akan mencari solusi yang menghindari switchlangkah pertama ...

Dean Harding
sumber
4
polimorfisme memiliki kelemahan memecah-mecah kode, membuatnya lebih sulit untuk dipahami dibandingkan berada di satu lokasi. Karenanya Anda mungkin agak ragu sebelum mengubahnya menjadi itu.
Mengapa tabel lompat / kamus lebih baik daripada saklar?
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;
}

Cara penulisan jenis sakelar jenis ini cukup umum. Alasan mengapa Anda merasakan sakelar ganti jika bulkier adalah karena tubuh Anda hanya satu garis dan dengan sakelar ganti Anda juga memerlukan pernyataan break. Jadi switch case memiliki ukuran tubuh dua kali lipat dari yang lain. Dengan kode yang lebih substansial, pernyataan break tidak akan menambah banyak ke tubuh. Untuk badan baris tunggal, itu adalah praktik umum untuk menulis kode di baris yang sama dengan pernyataan kasus.

Seperti yang orang lain telah sebutkan, kasus saklar membuat maksud lebih jelas, Anda ingin membuat keputusan berdasarkan nilai variabel / ekspresi tunggal. Komentar saya murni dari sudut pandang keterbacaan dan tidak berbasis kinerja.

ayah
sumber
1
Jika Anda meletakkan sakelar dalam suatu metode dan membuat setiap case returnstring yang tepat, Anda dapat menghilangkan breakpernyataan.
Robert Harvey
Solusi Anda juga yang tercepat.
Titou
8

Dalam hal ini, pernyataan beralih lebih jelas cocok dengan maksud kode: pilih tindakan yang akan diambil berdasarkan nilai tunggal.

Sebaliknya, pernyataan if jauh lebih sulit dibaca - Anda harus melihat semuanya untuk memastikan apa yang terjadi. Bagi saya ini adalah kode yang lebih sedikit (walaupun jumlah karakter mungkin sedikit lebih tinggi) karena ada sedikit penguraian mental.

FinnNk
sumber
8

Saya setuju dengan Jerry bahwa serangkaian string lebih baik untuk kasus khusus ini, tetapi secara umum lebih baik menggunakan pernyataan switch / case daripada rantai elseifs. Ini lebih mudah dibaca, dan kadang-kadang kompiler dapat melakukan pekerjaan yang lebih baik untuk mengoptimalkan cara itu, tetapi ada manfaat lain juga: itu jauh lebih mudah untuk di-debug.

Ketika Anda menekan tombol itu, Anda hanya perlu melangkah satu kali untuk berakhir di cabang kanan, daripada dengan hati-hati melangkahi beberapa pernyataan jika satu per satu, dan mungkin menekan tombol terlalu cepat dan melangkah melewatinya dan kehilangan sesuatu dan memiliki untuk memulai.

Mason Wheeler
sumber
3

Saya lebih suka beralih dalam kasus-kasus semacam itu, itu cocok dengan titik kode yang lebih baik, menjalankan pernyataan yang berbeda untuk setiap nilai input yang berbeda. The if..elsebertindak lebih seperti "trik" untuk mencapai efek yang sama.

switch pernyataan juga lebih bersih, mudah untuk kesalahan ketik tersembunyi di semua itu ==

Juga, untuk blok besar di C, sakelar lebih cepat.

else..ifbisa lebih tepat ketika Anda memiliki rentang seperti (antara 1 dan 100, lakukan ini, antara 100 dan 200 melakukannya), atau dalam C, ketika Anda mencoba untuk beralih dengan elemen seperti string (yang mungkin dalam bahasa lain). Itu sama saja.

Saya cenderung menggunakan banyak sakelar ketika saya memprogram dalam C.

Khelben
sumber
2

Pilih sesuatu yang efisien, singkat, dan kemudian dokumentasikan tidak hanya apa yang telah Anda lakukan, tetapi mengapa.

Kode dapat ditinjau kembali, dan tidak selalu oleh penulis aslinya.

Ada saat-saat ketika Anda mungkin dengan sengaja memilih satu implementasi dari yang lain karena Anda berpikiran maju tentang kode yang tidak ada.

Walt Stoneburner
sumber
2

Saya biasanya tidak suka pendekatan mana pun. Switch lama atau jika pernyataan hanya meminta untuk di refactored ke abstraksi berorientasi objek (namun contoh Anda, saya akan mengklasifikasikan sebagai pendek, tidak panjang).

Saya pribadi akan membungkus kode semacam itu ke dalam metode pembantu terpisah.

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

Menempatkan sakelar dalam metode terpisah memungkinkan Anda untuk menempatkan pernyataan pengembalian langsung di dalam pernyataan sakelar (setidaknya dalam c #), menghilangkan kebutuhan untuk pernyataan break juga, membuat kode lebih mudah dibaca.

Dan ini jauh lebih bagus daripada pendekatan if / else if / else if.

Pete
sumber
3
Saya pribadi benci cara "memasukkannya ke metode lain karena kelihatannya jelek" untuk menyelesaikan masalah. Daftar metode clutters dan IMHO terlihat lebih buruk. Saya hanya akan melakukan ini jika A) kode digandakan di suatu tempat atau B) Dapat berguna di tempat lain
TheLQ
1
daftar metode apa? dan mengapa daftar metode Anda berantakan lebih buruk daripada kode Anda berantakan? Saya pikir kami bergerak melewati usia "simpan semuanya dalam satu metode tunggal sehingga Anda dapat melihat semuanya sekaligus"
sara
@TheLQ Saya setuju dengan Anda secara umum, tetapi dalam kasus ini "comment =" memang diperhitungkan oleh proposal Pete.
Titou
0

Dalam python, tidak ada pernyataan switch, karena jika / elif / else bagus:

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"

Sederhana bukan?

Christopher Mahan
sumber
Elifhanyalah pernyataan if dengan beberapa huruf yang hilang. Ini jelas lebih seperti ifpernyataan daripada pernyataan beralih. Fakta bahwa Python TIDAK memiliki saklar membuat orang yang membenci mereka (seperti saya) berpikir bahwa mereka tidak sendirian.
Dan Rosenstark
Pemformatan python bekerja di stackoverflow tetapi tidak di programmers.stackexchange.com :(
Christopher Mahan
Anda harus memberi tahu mereka metakecuali topik yang diketahui. Terima kasih telah memberikan saya saksi.
Dan Rosenstark
ya, tahu mereka memilikinya di meta.programmers.stackexchange.com/questions/308/…
Christopher Mahan
1
@Yar, Mengingatkan saya pada hari admin wikipedia saya ... Oh Joy. (Apakah saya benar-benar di luar topik?)
Christopher Mahan
0

Salah satu hal yang membuat gaya C / C # switchsangat mengganggu adalah desakan pada casenilai menjadi literal. Satu hal yang menyenangkan tentang VB / VB.NET adalah select/casememungkinkan setiap case menjadi ekspresi boolean. Itu nyaman. Sejauh serangkaian ekspresi boolean yang saling eksklusif sering membantu, serangkaian if / else ifs lebih fleksibel, belum lagi lebih efisien untuk mengetik dan membaca.

Joel Brown
sumber