Keuntungan beralih pernyataan if-else

168

Apa praktik terbaik untuk menggunakan switchpernyataan vs menggunakan ifpernyataan untuk 30 unsignedpenghitungan di mana sekitar 10 memiliki tindakan yang diharapkan (yang saat ini adalah tindakan yang sama). Kinerja dan ruang perlu dipertimbangkan tetapi tidak kritis. Saya telah mengambil cuplikannya jadi jangan membenci saya karena konvensi penamaan.

switch pernyataan:

// numError is an error enumeration type, with 0 being the non-error case
// fire_special_event() is a stub method for the shared processing

switch (numError)
{  
  case ERROR_01 :  // intentional fall-through
  case ERROR_07 :  // intentional fall-through
  case ERROR_0A :  // intentional fall-through
  case ERROR_10 :  // intentional fall-through
  case ERROR_15 :  // intentional fall-through
  case ERROR_16 :  // intentional fall-through
  case ERROR_20 :
  {
     fire_special_event();
  }
  break;

  default:
  {
    // error codes that require no additional action
  }
  break;       
}

if pernyataan:

if ((ERROR_01 == numError)  ||
    (ERROR_07 == numError)  ||
    (ERROR_0A == numError)  || 
    (ERROR_10 == numError)  ||
    (ERROR_15 == numError)  ||
    (ERROR_16 == numError)  ||
    (ERROR_20 == numError))
{
  fire_special_event();
}
Semangat-
sumber
26
Ini diedit sebagai 'subyektif'? Betulkah? Tentunya 'subyektif' adalah untuk hal-hal yang tidak dapat dibuktikan dengan satu atau lain cara?
Alexandra Franks
Tentu Anda dapat melihatnya dari titik yang menghasilkan kode paling efisien, tetapi setiap kompiler modern harus sama efisiennya. Pada akhirnya, ini lebih merupakan pertanyaan tentang warna gudang sepeda.
jfs
8
Saya tidak setuju, saya tidak berpikir ini subjektif. Perbedaan ASM yang sederhana penting, Anda tidak bisa mengabaikan beberapa detik pengoptimalan dalam banyak kasus. Dan dalam pertanyaan ini, ini bukan perang agama atau perdebatan, ada penjelasan rasional mengapa seseorang lebih cepat, baca saja jawaban yang diterima.
chakrit
1
Yang lebih cepat: stackoverflow.com/questions/6805026/is-switch-faster-than-if
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功 法轮功
@RichardFranks offtopic: grats! Anda manusia pertama yang mengambil alih moderasi pada SO yang pernah saya lihat
jungle_mole

Jawaban:

162

Gunakan sakelar.

Dalam kasus terburuk kompiler akan menghasilkan kode yang sama dengan rantai if-else, sehingga Anda tidak kehilangan apa pun. Jika ragu, masukkan kasus yang paling umum terlebih dahulu ke pernyataan switch.

Dalam kasus terbaik pengoptimal mungkin menemukan cara yang lebih baik untuk menghasilkan kode. Hal-hal umum yang dilakukan oleh kompiler adalah membangun pohon keputusan biner (menyimpan perbandingan dan melompat dalam kasus rata-rata) atau cukup membangun tabel melompat (berfungsi tanpa perbandingan sama sekali).

Nils Pipenbrinck
sumber
2
Secara teknis masih akan ada satu perbandingan, untuk memastikan nilai enum terletak di dalam tabel lompatan.
0124816
Jep. Itu benar. Mengaktifkan enum dan menangani semua case dapat menyingkirkan pembandingan terakhir.
Nils Pipenbrinck
4
Perhatikan bahwa serangkaian if secara teoritis dapat dianalisis sama dengan switch oleh kompiler, tetapi mengapa mengambil risiko? Dengan menggunakan sakelar, Anda mengkomunikasikan apa yang Anda inginkan, yang membuat pembuatan kode lebih mudah.
jakobengblom2
5
jakoben: Itu bisa dilakukan, tetapi hanya untuk rantai switch-like / else. Dalam prakteknya ini tidak terjadi karena programmer menggunakan saklar. Saya menggali ke dalam teknologi kompiler dan mempercayai saya: Menemukan konstruksi "tidak berguna" seperti itu membutuhkan banyak waktu. Untuk kompiler, optimasi seperti itu tidak masuk akal.
Nils Pipenbrinck
5
@NilsPipenbrinck dengan kemudahan membangun pseudo-rekursif if- elserantai dalam template meta pemrograman, dan kesulitan menghasilkan switch caserantai, pemetaan yang mungkin menjadi lebih penting. (dan ya, komentar kuno, tetapi web itu selamanya, atau setidaknya sampai selasa berikutnya)
Yakk - Adam Nevraumont
45

Untuk kasus khusus yang Anda berikan dalam contoh Anda, kode yang paling jelas mungkin:

if (RequiresSpecialEvent(numError))
    fire_special_event();

Jelas ini hanya memindahkan masalah ke area kode yang berbeda, tetapi sekarang Anda memiliki kesempatan untuk menggunakan kembali tes ini. Anda juga memiliki lebih banyak opsi untuk menyelesaikannya. Anda dapat menggunakan std :: set, misalnya:

bool RequiresSpecialEvent(int numError)
{
    return specialSet.find(numError) != specialSet.end();
}

Saya tidak menyarankan bahwa ini adalah implementasi terbaik dari MemerlukanSpecialEvent, hanya saja itu pilihan. Anda masih bisa menggunakan switch atau rantai if-else, atau tabel pencarian, atau manipulasi bit pada nilainya, apa pun. Semakin tidak jelas proses keputusan Anda, semakin banyak nilai yang akan Anda dapatkan dari memilikinya dalam fungsi yang terisolasi.

Mark tebusan
sumber
5
Ini sangat benar. Keterbacaan jauh lebih baik daripada saklar dan pernyataan-if. Saya sebenarnya akan menjawab sesuatu seperti ini sendiri, tetapi Anda mengalahkan saya untuk itu. :-)
mlarsen
Jika nilai enum Anda semua kecil, maka Anda tidak perlu hash, hanya sebuah tabel. mis. const std::bitset<MAXERR> specialerror(initializer); Gunakan dengan if (specialerror[numError]) { fire_special_event(); }. Jika Anda ingin memeriksa batas, bitset::test(size_t)akan memberikan pengecualian pada nilai di luar batas. ( bitset::operator[]tidak rentang-periksa). cplusplus.com/reference/bitset/bitset/test . Ini mungkin akan mengungguli implementasi tabel lompatan yang dihasilkan compiler switch, esp. dalam kasus tidak-khusus di mana ini akan menjadi cabang tunggal yang tidak diambil.
Peter Cordes
@PeterCordes Saya masih berpendapat bahwa lebih baik meletakkan tabel ke dalam fungsinya sendiri. Seperti yang saya katakan, ada banyak opsi yang terbuka ketika Anda melakukan itu, saya tidak mencoba untuk menghitung semuanya.
Mark Ransom
@ Markarkhans: Saya tidak bermaksud tidak setuju dengan abstrak itu. Hanya sejak Anda memberikan implementasi sampel menggunakan std::set, saya pikir saya akan menunjukkan bahwa itu mungkin pilihan yang buruk. Ternyata gcc sudah mengkompilasi kode OP untuk menguji bitmap dalam 32bit langsung. godbolt: goo.gl/qjjv0e . gcc 5.2 bahkan akan melakukan ini untuk ifversi. Juga, gcc yang lebih baru akan menggunakan instruksi uji bit btalih-alih bergeser untuk meletakkan 1sedikit di tempat yang tepat dan menggunakan test reg, imm32.
Peter Cordes
Bitmap langsung-konstan ini adalah kemenangan besar, karena tidak ada cache yang ketinggalan pada bitmap. Ini bekerja jika kode kesalahan "khusus" semua dalam kisaran 64 atau kurang. (atau 32 untuk kode 32bit lama.) Kompiler mengurangi nilai case terkecil, jika bukan nol. Yang perlu diperhatikan adalah kompiler baru cukup pintar sehingga Anda mungkin akan mendapatkan kode yang baik dari logika apa pun yang Anda gunakan, kecuali Anda mengatakannya untuk menggunakan struktur data yang besar.
Peter Cordes
24

Switch adalah lebih cepat.

Coba saja jika ada 30 nilai berbeda di dalam satu loop, dan bandingkan dengan kode yang sama menggunakan sakelar untuk melihat seberapa cepat sakelar itu.

Sekarang, switch memiliki satu masalah nyata : Switch harus tahu pada waktu kompilasi nilai-nilai di dalam setiap case. Ini berarti kode berikut:

// WON'T COMPILE
extern const int MY_VALUE ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

tidak akan dikompilasi.

Kebanyakan orang kemudian akan menggunakan mendefinisikan (Aargh!), Dan yang lain akan mendeklarasikan dan mendefinisikan variabel konstan dalam unit kompilasi yang sama. Sebagai contoh:

// WILL COMPILE
const int MY_VALUE = 25 ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

Jadi, pada akhirnya, pengembang harus memilih antara "speed + clarity" vs. "code coupling".

(Bukan berarti sakelar tidak bisa ditulis untuk membingungkan sekali ... Kebanyakan sakelar yang saya lihat saat ini termasuk kategori "membingungkan" "... Tapi ini adalah cerita lain ...)

Edit 2008-09-21:

bk1e menambahkan komentar berikut: " Mendefinisikan konstanta sebagai enum dalam file header adalah cara lain untuk menangani ini".

Tentu saja.

Titik tipe extern adalah untuk memisahkan nilai dari sumber. Mendefinisikan nilai ini sebagai makro, sebagai deklarasi konst int sederhana, atau bahkan sebagai enum memiliki efek samping dari inlining nilai. Jadi, seandainya mendefinisikan, nilai enum, atau nilai konstanta berubah, sebuah kompilasi akan diperlukan. Deklarasi eksternal berarti tidak perlu mengkompilasi ulang jika terjadi perubahan nilai, tetapi di sisi lain, membuatnya tidak mungkin untuk menggunakan sakelar. Kesimpulannya adalah Menggunakan sakelar akan meningkatkan sambungan antara kode sakelar dan variabel yang digunakan sebagai kasing . Ketika OK, maka gunakan sakelar. Kalau tidak, maka tidak mengejutkan.

.

Edit 2013-01-15:

Vlad Lazarenko mengomentari jawaban saya, memberikan tautan ke studi mendalam tentang kode perakitan yang dihasilkan oleh sebuah saklar. Sangat mencerahkan: http://lazarenko.me/switch/

paercebal
sumber
Menentukan konstanta sebagai enum dalam file header adalah cara lain untuk menangani hal ini.
bk1e
1
@Vlad Lazarenko: Terima kasih atas tautannya! Itu bacaan yang sangat menarik.
paercebal
1
Tautan @AhmedHussein user404725 sudah mati. Untungnya, saya menemukannya di WayBack Machine: web.archive.org/web/20131111091431/http://lazarenko.me/2013/01/… . Memang, Mesin WayBack bisa menjadi berkah.
Jack Giffin
Terima kasih banyak, ini sangat membantu
Ahmed Hussein
20

Kompiler akan tetap mengoptimalkannya - gunakan sakelar karena itu yang paling mudah dibaca.

Alexandra Franks
sumber
3
Kemungkinan kompiler tidak akan menyentuh if-then-else. Bahkan, gcctidak akan melakukan itu dengan pasti (ada alasan bagus untuk itu). Dentang akan mengoptimalkan kedua kasus menjadi pencarian biner. Sebagai contoh, lihat ini .
7

Switch, jika hanya untuk keterbacaan. Raksasa jika pernyataan lebih sulit dipertahankan dan sulit dibaca menurut saya.

ERROR_01 : // fall-through yang disengaja

atau

(ERROR_01 == numError) ||

Yang belakangan lebih rentan kesalahan dan membutuhkan lebih banyak pengetikan dan pemformatan daripada yang pertama.

scubabbl
sumber
6

Kode untuk keterbacaan. Jika Anda ingin tahu apa yang berkinerja lebih baik, gunakan profiler, karena pengoptimalan dan penyusun berbeda-beda, dan masalah kinerja jarang terjadi di mana orang berpikir seperti itu.

Bdoserror
sumber
6

Gunakan switch, ini untuk apa dan apa yang diharapkan oleh programmer.

Saya akan memasukkan label case yang berlebihan - hanya untuk membuat orang merasa nyaman, saya mencoba mengingat kapan / apa aturannya untuk tidak menggunakannya.
Anda tidak ingin programmer berikutnya yang mengerjakannya harus melakukan pemikiran yang tidak perlu tentang perincian bahasa (mungkin Anda dalam waktu beberapa bulan!)

Martin Beckett
sumber
4

Compiler sangat bagus dalam mengoptimalkan switch. Gcc terbaru juga baik dalam mengoptimalkan banyak kondisi dalam if.

Saya membuat beberapa test case di godbolt .

Ketika case nilai dikelompokkan berdekatan, gcc, clang, dan icc semuanya cukup pintar untuk menggunakan bitmap untuk memeriksa apakah suatu nilai adalah salah satu yang istimewa.

misalnya gcc 5.2 -O3 mengkompilasi switchto (dan ifsesuatu yang sangat mirip):

errhandler_switch(errtype):  # gcc 5.2 -O3
    cmpl    $32, %edi
    ja  .L5
    movabsq $4301325442, %rax   # highest set bit is bit 32 (the 33rd bit)
    btq %rdi, %rax
    jc  .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

Perhatikan bahwa bitmap adalah data langsung, jadi tidak ada potensi data-cache ketinggalan mengaksesnya, atau tabel lompatan.

gcc 4.9.2 -O3 mengkompilasi switchke bitmap, tetapi melakukan 1U<<errNumberdengan mov / shift. Ini mengkompilasi ifversi ke serangkaian cabang.

errhandler_switch(errtype):  # gcc 4.9.2 -O3
    leal    -1(%rdi), %ecx
    cmpl    $31, %ecx    # cmpl $32, %edi  wouldn't have to wait an extra cycle for lea's output.
              # However, register read ports are limited on pre-SnB Intel
    ja  .L5
    movl    $1, %eax
    salq    %cl, %rax   # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
    testl   $2150662721, %eax
    jne .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

Perhatikan cara mengurangi 1 dari errNumber(dengan leamenggabungkan operasi itu dengan gerakan). Itu memungkinkannya menyesuaikan bitmap menjadi 32bit segera, menghindari 64bit-langsungmovabsq yang membutuhkan lebih banyak byte instruksi.

Urutan yang lebih pendek (dalam kode mesin) adalah:

    cmpl    $32, %edi
    ja  .L5
    mov     $2150662721, %eax
    dec     %edi   # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
    bt     %edi, %eax
    jc  fire_special_event
.L5:
    ret

(Kegagalan untuk menggunakan jc fire_special_eventada di mana-mana, dan merupakan bug penyusun .)

rep retdigunakan dalam target cabang, dan mengikuti cabang bersyarat, untuk kepentingan AMD K8 dan K10 lama (pra-Bulldozer): Apa arti `rep ret`? . Tanpanya, prediksi cabang tidak akan bekerja dengan baik pada CPU yang usang.

bt(uji bit) dengan register arg cepat. Ini menggabungkan pekerjaan menggeser kiri 1 demi errNumberbit dan melakukan atest , tetapi masih 1 siklus latensi dan hanya satu Intel uop. Ini lambat dengan memori arg karena caranya terlalu-semantik CISC: dengan operan memori untuk "bit string", alamat byte yang akan diuji dihitung berdasarkan argumen lain (dibagi dengan 8), dan tidak terbatas pada potongan 1, 2, 4, atau 8 byte yang ditunjukkan oleh operan memori.

Dari tabel instruksi Agner Fog, instruksi shift penghitungan variabel lebih lambat dari btpada Intel terbaru (2 uops, bukan 1, dan shift tidak melakukan semua hal lain yang diperlukan).

Peter Cordes
sumber
4

Keuntungan dari sakelar () dibandingkan jika kasing lain adalah: - 1. Sakelar jauh lebih efisien dibandingkan jika kasing karena setiap kasing tidak tergantung pada kasing sebelumnya tidak seperti jika kasing terpisah harus diperiksa untuk kondisi benar atau salah.

  1. Ketika ada no. dari nilai-nilai untuk ekspresi tunggal, sakelar kasus lebih fleksibel daripada jika lain karena dalam jika lain penilaian kasus didasarkan pada hanya dua nilai yaitu benar atau salah.

  2. Nilai-nilai di saklar ditentukan oleh pengguna sedangkan nilai-nilai di dalam kasus lain didasarkan pada kendala.

  3. Jika terjadi kesalahan, pernyataan dalam sakelar dapat dengan mudah diperiksa dan dikoreksi yang relatif sulit untuk diperiksa jika ada pernyataan if else.

  4. Switch case jauh lebih kompak dan mudah dibaca dan dimengerti.

Vineeth Krishna K
sumber
2

IMO ini adalah contoh sempurna dari apa saklar jatuh dibuat untuk.

Nescio
sumber
di c # ini adalah satu-satunya kasus di mana pemikiran jatuh terjadi. Argumen yang bagus di sana.
BCS
2

Jika kasus Anda cenderung tetap dikelompokkan di masa mendatang - jika lebih dari satu kasus sesuai dengan satu hasil - peralihan mungkin terbukti lebih mudah dibaca dan dirawat.

TSomKes
sumber
2

Mereka bekerja sama baiknya. Performanya hampir sama diberikan kompiler modern.

Saya lebih suka jika pernyataan daripada pernyataan kasus karena lebih mudah dibaca, dan lebih fleksibel - Anda dapat menambahkan kondisi lain yang tidak didasarkan pada kesetaraan numerik, seperti "|| max <min". Tetapi untuk kasus sederhana yang Anda posting di sini, itu tidak masalah, lakukan saja apa yang paling mudah dibaca oleh Anda.

SquareCog
sumber
2

saklar pasti disukai. Lebih mudah untuk melihat daftar kasus switch & tahu pasti apa yang dilakukannya daripada membaca kondisi jika panjang.

Duplikasi dalam ifkondisi sulit di mata. Misalkan salah satu ==ditulis !=; apakah kamu memperhatikan? Atau jika satu contoh 'numError' ditulis 'nmuError', yang kebetulan dikompilasi?

Saya biasanya lebih suka menggunakan polimorfisme alih-alih beralih, tetapi tanpa lebih detail konteksnya, sulit dikatakan.

Adapun kinerja, taruhan terbaik Anda adalah menggunakan profiler untuk mengukur kinerja aplikasi Anda dalam kondisi yang mirip dengan apa yang Anda harapkan di alam liar. Kalau tidak, Anda mungkin mengoptimalkan di tempat yang salah dan dengan cara yang salah.

Jay Bazuzi
sumber
2

Saya setuju dengan kemampuan solusi beralih tetapi IMO Anda membajak sakelar di sini.
Tujuan sakelar adalah memiliki penanganan yang berbeda tergantung pada nilainya.
Jika Anda harus menjelaskan algo Anda dalam pseudo-code, Anda akan menggunakan if karena, secara semantik, begitulah: jika whatever_error melakukan ini ...
Jadi kecuali jika suatu hari Anda bermaksud mengubah kode Anda untuk memiliki kode spesifik untuk setiap kesalahan , Saya akan gunakan jika .

Francesca
sumber
2
Saya tidak setuju, karena alasan yang sama saya tidak setuju dengan kasus jatuh-pikir. Saya membaca saklar sebagai "Dalam kasus 01,07,0A, 10,15,16, dan 20 peristiwa khusus kebakaran." Tidak ada kejatuhan ke bagian lain., Ini hanya artefak dari sintaks C ++ di mana Anda mengulangi kata kunci 'case' untuk setiap nilai.
MSalters
1

Saya akan memilih pernyataan if demi kejelasan dan konvensi, meskipun saya yakin beberapa akan tidak setuju. Lagi pula, Anda ingin melakukan sesuatu ifsuatu kondisi yang benar! Berganti dengan satu tindakan sepertinya sedikit ... tidak perlu.

William Keller
sumber
1

Saya akan mengatakan menggunakan SWITCH. Dengan cara ini Anda hanya perlu menerapkan hasil yang berbeda. Sepuluh kasing identik Anda dapat menggunakan default. Jika satu perubahan yang Anda perlukan adalah menerapkan perubahan secara eksplisit, tidak perlu mengedit default. Jauh lebih mudah untuk menambah atau menghapus kasus dari SWITCH daripada mengedit IF dan ELSEIF.

switch(numerror){
    ERROR_20 : { fire_special_event(); } break;
    default : { null; } break;
}

Mungkin bahkan menguji kondisi Anda (dalam hal ini numerror) terhadap daftar kemungkinan, sebuah array mungkin jadi SWITCH Anda bahkan tidak digunakan kecuali pasti akan ada hasilnya.

Lewis
sumber
Ada sekitar 30 kesalahan total. 10 memerlukan tindakan khusus, jadi saya menggunakan default untuk ~ 20 kesalahan yang tidak memerlukan tindakan ...
Zing-
1

Karena Anda hanya memiliki 30 kode kesalahan, buat sendiri tabel lompatan Anda sendiri, lalu buat sendiri semua pilihan pengoptimalan (lompatan akan selalu tercepat), daripada berharap kompiler akan melakukan hal yang benar. Itu juga membuat kode sangat kecil (terlepas dari deklarasi statis dari tabel lompat). Ini juga memiliki keuntungan sampingan bahwa dengan debugger Anda dapat memodifikasi perilaku saat runtime harus Anda butuhkan, hanya dengan menyodok data tabel secara langsung.

Greg Whitfield
sumber
Wow, itu sepertinya cara untuk mengubah masalah sederhana menjadi masalah yang kompleks. Mengapa harus repot-repot ketika kompiler akan melakukan pekerjaan yang bagus untuk Anda. Plus itu tampaknya penangan kesalahan, jadi sepertinya tidak terlalu kritis. Pergantian sejauh ini merupakan hal yang paling mudah untuk dibaca dan dipelihara.
MrZebra
Sebuah tabel hampir tidak rumit - bahkan mungkin lebih sederhana daripada beralih ke kode. Dan pernyataan itu menyebutkan kinerja adalah faktor.
Greg Whitfield
Kedengarannya seperti optimasi prematur. Selama Anda menjaga nilai enum Anda kecil dan berdekatan, kompiler harus melakukannya untuk Anda. Menempatkan sakelar dalam fungsi terpisah membuat kode yang menggunakannya bagus dan kecil, seperti yang disarankan Mark Ransom dalam jawabannya, memberikan manfaat kode kecil yang sama.
Peter Cordes
Juga, jika Anda akan mengimplementasikan sendiri sesuatu, buatlah std::bitset<MAXERR> specialerror;, lalu if (specialerror[err]) { special_handler(); }. Ini akan lebih cepat daripada tabel lompat, esp. dalam kasus yang tidak diambil.
Peter Cordes
1

Saya tidak yakin tentang praktik terbaik, tapi saya akan menggunakan sakelar - dan kemudian menjebak secara sengaja melalui 'default'

da5id
sumber
1

Secara estetika saya cenderung menyukai pendekatan ini.

unsigned int special_events[] = {
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20
 };
 int special_events_length = sizeof (special_events) / sizeof (unsigned int);

 void process_event(unsigned int numError) {
     for (int i = 0; i < special_events_length; i++) {
         if (numError == special_events[i]) {
             fire_special_event();
             break;
          }
     }
  }

Jadikan data sedikit lebih pintar sehingga kita bisa membuat logikanya sedikit bodoh.

Saya menyadari itu terlihat aneh. Inilah inspirasinya (dari bagaimana saya melakukannya dengan Python):

special_events = [
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20,
    ]
def process_event(numError):
    if numError in special_events:
         fire_special_event()
mbac32768
sumber
4
Sintaks bahasa memang memiliki efek pada bagaimana kami menerapkan solusi ... => Kelihatannya jelek di C dan bagus di Python. :)
rlerallut
Gunakan bitmap? Jika error_0a adalah 0x0a dll Anda bisa menempatkannya dalam bit yang panjang. long long special_events = 1LL << 1 | 1LL << 7 | 1LL << 0xa ... Kemudian gunakan if (special_events & (1LL << numError) fire_special_event ()
paperhorse
1
Yuck. Anda telah mengubah operasi kasus terburuk O (1) (jika tabel lompat dihasilkan) menjadi kasus terburuk O (N) (di mana N adalah jumlah kasus yang ditangani), dan Anda menggunakan breakluar case((ya, minor) dosa, tapi tetap saja dosa). :)
Mac
Ya Dia mengatakan kinerja dan ruang tidak kritis. Saya hanya mengusulkan cara lain untuk melihat masalah. Jika kita bisa merepresentasikan masalah dengan cara manusia berpikir kurang, maka saya biasanya tidak peduli apakah itu berarti komputer harus berpikir lebih banyak.
mbac32768
1
while (true) != while (loop)

Mungkin yang pertama dioptimalkan oleh kompiler, yang akan menjelaskan mengapa loop kedua lebih lambat saat meningkatkan jumlah loop.

MarioFrost
sumber
Tampaknya ini adalah komentar untuk jawaban McAnix. Itu hanya salah satu masalah dengan upaya pengaturan waktu ifvs switchsebagai kondisi loop-end di Jawa.
Peter Cordes
1

Ketika datang untuk mengkompilasi program, saya tidak tahu apakah ada perbedaan. Tetapi untuk program itu sendiri dan menjaga kode sesederhana mungkin, saya pribadi pikir itu tergantung pada apa yang ingin Anda lakukan. jika tidak, jika tidak, pernyataan memiliki kelebihan, yang menurut saya adalah:

memungkinkan Anda untuk menguji variabel terhadap rentang tertentu Anda dapat menggunakan fungsi (Perpustakaan Standar atau Pribadi) sebagai persyaratan.

(contoh:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 if( a > 0 && a < 5)
   {
     cout<<"a is between 0, 5\n";

   }else if(a > 5 && a < 10)

     cout<<"a is between 5,10\n";

   }else{

       "a is not an integer, or is not in range 0,10\n";

Namun, jika tidak, jika lain pernyataan bisa menjadi rumit dan berantakan (meskipun upaya terbaik Anda) terburu-buru. Pergantian pernyataan cenderung lebih jelas, lebih bersih, dan lebih mudah dibaca; tetapi hanya dapat digunakan untuk menguji terhadap nilai-nilai spesifik (contoh:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 switch(a)
 {
    case 0:
    case 1:
    case 2: 
    case 3:
    case 4:
    case 5:
        cout<<"a is between 0,5 and equals: "<<a<<"\n";
        break;
    //other case statements
    default:
        cout<<"a is not between the range or is not a good value\n"
        break;

Saya lebih suka pernyataan if - else if - else, tapi itu terserah Anda. Jika Anda ingin menggunakan fungsi sebagai kondisi, atau Anda ingin menguji sesuatu terhadap rentang, array, atau vektor dan / atau Anda tidak keberatan berurusan dengan sarang yang rumit, saya akan merekomendasikan menggunakan If else if else blok. Jika Anda ingin menguji terhadap nilai tunggal atau Anda ingin blok yang bersih dan mudah dibaca, saya akan merekomendasikan Anda menggunakan switch () blok kasus.

Jordan Effinger
sumber
0

Saya bukan orang yang memberitahu Anda tentang kecepatan dan penggunaan memori, tetapi melihat statment switch adalah jauh lebih mudah untuk dipahami daripada pernyataan if besar (terutama 2-3 bulan kemudian)

Ed Brown
sumber
0

Saya tahu yang lama tapi

public class SwitchTest {
static final int max = 100000;

public static void main(String[] args) {

int counter1 = 0;
long start1 = 0l;
long total1 = 0l;

int counter2 = 0;
long start2 = 0l;
long total2 = 0l;
boolean loop = true;

start1 = System.currentTimeMillis();
while (true) {
  if (counter1 == max) {
    break;
  } else {
    counter1++;
  }
}
total1 = System.currentTimeMillis() - start1;

start2 = System.currentTimeMillis();
while (loop) {
  switch (counter2) {
    case max:
      loop = false;
      break;
    default:
      counter2++;
  }
}
total2 = System.currentTimeMillis() - start2;

System.out.println("While if/else: " + total1 + "ms");
System.out.println("Switch: " + total2 + "ms");
System.out.println("Max Loops: " + max);

System.exit(0);
}
}

Memvariasikan jumlah loop berubah banyak:

Sementara jika / selain itu: 5ms Switch: 1ms Max Loops: 100000

Sedangkan jika / selain itu: 5ms Switch: 3ms Max Loops: 1000000

Sementara jika / selain itu: 5ms Switch: 14ms Max Loops: 10000000

Sedangkan jika / selain itu: 5ms Switch: 149ms Max Loops: 100000000

(tambahkan lebih banyak pernyataan jika Anda mau)

McAnix
sumber
3
Poin bagus, tapi sry, kawan, kamu dalam bahasa yang salah. Memvariasikan bahasa banyak berubah;)
Gabriel Schreiber
2
The if(max) breakloop berjalan dalam waktu yang konstan terlepas dari jumlah lingkaran? Kedengarannya seperti kompiler JIT cukup pintar untuk mengoptimalkan loop ke counter2=max. Dan mungkin itu lebih lambat daripada beralih jika panggilan pertama ke currentTimeMillismemiliki lebih banyak overhead, karena belum semuanya dikompilasi dengan JIT? Menempatkan loop di urutan lain mungkin akan memberikan hasil yang berbeda.
Peter Cordes