Ganti pernyataan untuk lebih dari / kurang dari


230

jadi saya ingin menggunakan pernyataan switch seperti ini:

switch (scrollLeft) {
  case (<1000):
   //do stuff
   break;
  case (>1000 && <2000):
   //do stuff
   break;
}

Sekarang saya tahu bahwa salah satu dari pernyataan tersebut ( <1000) atau ( >1000 && <2000) tidak akan berfungsi (untuk alasan yang berbeda, jelas). Yang saya tanyakan adalah cara paling efisien untuk melakukan hal itu. Saya benci menggunakan 30 ifpernyataan, jadi saya lebih suka menggunakan sintaks saklar. Adakah yang bisa saya lakukan?


5
apakah langkah Anda teratur? Maksud saya, jika Anda membagi scrollLeft dengan 1000, Anda dapat beralih 1, 2, 3 ...
IcanDivideBy0

Mungkin Anda bisa membuat array yang diurutkan yang memetakan rentang kondisi dengan operasi yang sesuai, dan menerapkan pencarian biner di atasnya. Atau jika kondisi Anda cukup teratur, Anda bisa langsung menelepon your_mapper_object[scrollLeft / SOME_CONST], dengan anggapan your_mapper_objectseperti itu {1: some_func, 2: another_func, ...}. Dan dalam hal ini Anda juga bisa menggunakan sakelar.
Overmind Jiang

Jawaban:


731

Ketika saya melihat solusi di jawaban lain saya melihat beberapa hal yang saya tahu buruk untuk kinerja. Saya akan memberi mereka komentar tetapi saya pikir lebih baik untuk membandingkannya dan membagikan hasilnya. Anda bisa mengujinya sendiri . Di bawah ini adalah hasil saya (ymmv) dinormalisasi setelah operasi tercepat di setiap browser (kalikan waktu 1.0 dengan nilai dinormalisasi untuk mendapatkan waktu absolut dalam ms).

                    Chrome Firefox Opera MSIE Safari Node
-------------------------------------------------- -----------------
1.0 waktu 37ms 73ms 68ms 184ms 73ms 21ms
jika-segera 1.0 1.0 1.0 2.6 1.0 1.0
jika-tidak langsung 1.2 1.8 3.3 3.8 2.6 1.0
switch-direct 2.0 1.1 2.0 1.0 2.8 1.3
saklar-kisaran 38.1 10.6 2.6 7.3 20.9 10.4
switch-range2 31.9 8.3 2.0 4.5 9.5 6.9
switch-indirect-array 35.2 9.6 4.2 5.5 10.7 8.6
array-linear-switch 3.6 4.1 4.5 10.0 4.7 2.7
array-binary-switch 7.8 6.7 9.5 16.0 15.0 4.9

Tes di mana dilakukan pada Windows 7 32bit dengan versi berikut: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Node dijalankan pada Linux 64bit box karena resolusi timer pada Node.js untuk Windows adalah 10ms, bukan 1ms.

jika-langsung

Ini adalah yang tercepat di semua lingkungan yang diuji, kecuali di ... drumroll MSIE! (kejutan kejutan). Ini adalah cara yang disarankan untuk mengimplementasikannya.

if (val < 1000) { /*do something */ } else
if (val < 2000) { /*do something */ } else
...
if (val < 30000) { /*do something */ } else

jika-tidak langsung

Ini adalah varian dari switch-indirect-arraytetapi dengan- ifpernyataan sebagai gantinya dan berkinerja lebih cepat daripada switch-indirect-arraydi hampir semua lingkungan yang diuji.

values=[
   1000,  2000, ... 30000
];
if (val < values[0]) { /* do something */ } else
if (val < values[1]) { /* do something */ } else
...
if (val < values[29]) { /* do something */ } else

beralih-langsung

Ini cukup cepat di semua lingkungan yang diuji, dan sebenarnya yang tercepat di MSIE. Ini berfungsi ketika Anda bisa melakukan perhitungan untuk mendapatkan indeks.

switch (Math.floor(val/1000)) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

saklar-rentang

Ini sekitar 6 hingga 40 kali lebih lambat daripada yang tercepat di semua lingkungan yang diuji kecuali untuk Opera yang membutuhkan waktu sekitar satu setengah kali. Ini lambat karena mesin harus membandingkan nilai dua kali untuk setiap kasus. Anehnya, Chrome membutuhkan waktu hampir 40 kali lebih lama untuk menyelesaikannya dibandingkan dengan operasi tercepat di Chrome, sedangkan MSIE hanya membutuhkan waktu 6 kali lebih lama. Tetapi perbedaan waktu aktual hanya 74ms mendukung MSIE di 1337ms (!).

switch (true) {
  case (0 <= val &&  val < 1000): /* do something */ break;
  case (1000 <= val &&  val < 2000): /* do something */ break;
  ...
  case (29000 <= val &&  val < 30000): /* do something */ break;
}

saklar-range2

Ini adalah varian switch-rangetetapi dengan hanya satu perbandingan per case dan karena itu lebih cepat, tetapi masih sangat lambat kecuali di Opera. Urutan pernyataan kasus penting karena mesin akan menguji setiap kasus dalam urutan kode sumber ECMAScript262: 5 12.11

switch (true) {
  case (val < 1000): /* do something */ break;
  case (val < 2000): /* do something */ break;
  ...
  case (val < 30000): /* do something */ break;
}

switch-tidak langsung-array

Dalam varian ini rentang disimpan dalam array. Ini lambat di semua lingkungan yang diuji dan sangat lambat di Chrome.

values=[1000,  2000 ... 29000, 30000];

switch(true) {
  case (val < values[0]): /* do something */ break;
  case (val < values[1]): /* do something */ break;
  ...
  case (val < values[29]): /* do something */ break;
}

array-linear-search

Ini adalah kombinasi dari pencarian linear nilai-nilai dalam array, dan pernyataan switch dengan nilai-nilai tetap. Alasan seseorang mungkin ingin menggunakan ini adalah ketika nilai tidak diketahui sampai runtime. Ini lambat di setiap lingkungan yang diuji, dan memakan waktu hampir 10 kali lebih lama dalam MSIE.

values=[1000,  2000 ... 29000, 30000];

for (sidx=0, slen=values.length; sidx < slen; ++sidx) {
  if (val < values[sidx]) break;
}

switch (sidx) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

array-binary-switch

Ini adalah varian array-linear-switchtetapi dengan pencarian biner. Sayangnya ini lebih lambat daripada pencarian linear. Saya tidak tahu apakah ini implementasi saya atau apakah pencarian linier lebih dioptimalkan. Bisa juga bahwa keyspace adalah ke kecil.

values=[0, 1000,  2000 ... 29000, 30000];

while(range) {
  range = Math.floor( (smax - smin) / 2 );
  sidx = smin + range;
  if ( val < values[sidx] ) { smax = sidx; } else { smin = sidx; }
}

switch (sidx) {
  case 0: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

Kesimpulan

Jika kinerja penting, gunakan ifpernyataan-atau switchdengan nilai langsung.


128
Jarang sekali melihat jawaban dengan banyak detail dan struktur yang rapi ini. Besar +1
Rick Donohoe

10
Besar +1 untuk penjelasan sisi kinerja dari masalah ini!
Zoltán Schmidt

16
Inilah alasan mengapa stackoverflow adalah salah satu tempat terbaik untuk jawaban. Ini adalah jawaban "abadi", pekerjaan bagus dan terima kasih untuk jsfiddle!
Jessy

1
info & penjelasan grt
JayKandari

3
Saya benar-benar berharap bisa +2, jawaban terinci seperti itu!
Kaspar Lee

96

Sebuah alternatif:

var scrollleft = 1000;
switch (true)
{
    case (scrollleft > 1000):
      alert('gt');
      break;
    case (scrollleft <= 1000):
      alert('lt');
      break; 
}

Demo: http://jsfiddle.net/UWYzr/


4
ini adalah solusi yang lebih bernilai. +1
IcanDivideBy0

1
Bukankah ini sama saja if(...) else if(...)? Ini memang menghindar iftetapi tidak terdengar seperti pengganti yang bagus untuk saya.
pimvdb

7
Meski anggun untuk dikodekan, ia sakit kinerja. Ini hampir 30 kali lebih lambat di Chrome daripada menggunakan- ifpernyataan. Lihat jawaban
sekitar

1
Namun, penalti kinerja seperti itu dapat diabaikan bila data yang ditangani tidak besar dan mungkin fungsinya hanya diterapkan, seperti memvalidasi input pengguna tunggal, kemudian keterbacaan dipilih daripada kinerja dalam kasus tersebut.
Jesús Franco

1
Ini persis apa yang saya cari. Terima kasih!
Ami Schreiber

23
switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Hanya berfungsi jika Anda memiliki langkah reguler ...

EDIT: karena solusi ini terus bertambah, saya harus memberi saran bahwa solusi mofolo adalah cara yang lebih baik


1
Saya menggunakan Math.round(scrollLeft/1000)omong-omong.
switz

@Switz - Perlu diingat bahwa 999 <1000 masuk dalam case 0 tetapi Math.round (999/1000) masuk ke case 1. Juga, ada kesalahan ketik di atas, dalam hal ini 1 adalah> = 1000, tidak hanya> 1000 .
Igor

Satu-satunya masalah dengan solusi mofolo adalah sekitar 30 kali lebih lambat di Chrome daripada yang dilakukan oleh IcanDivideBy0. Lihat jawaban saya di bawah ini.
beberapa

6

Anda dapat membuat objek khusus dengan kriteria dan fungsi yang sesuai dengan kriteria

var rules = [{ lowerLimit: 0,    upperLimit: 1000, action: function1 }, 
             { lowerLimit: 1000, upperLimit: 2000, action: function2 }, 
             { lowerLimit: 2000, upperLimit: 3000, action: function3 }];

Tetapkan fungsi untuk apa yang ingin Anda lakukan dalam kasus ini (define function1, function2 dll)

Dan "mengevaluasi" aturan

function applyRules(scrollLeft)
{
   for(var i=0; i>rules.length; i++)
   {
       var oneRule = rules[i];
       if(scrollLeft > oneRule.lowerLimit && scrollLeft < oneRule.upperLimit)
       {
          oneRule.action();
       }
   }
}

Catatan

Saya benci menggunakan 30 jika pernyataan

Sering kali jika pernyataan lebih mudah dibaca dan dikelola. Saya akan merekomendasikan di atas hanya ketika Anda memiliki banyak kondisi dan kemungkinan banyak pertumbuhan di masa depan.

Perbarui
Seperti @Brad tunjukkan dalam komentar, jika kondisinya saling eksklusif (hanya satu yang benar pada satu waktu), memeriksa batas atas harus memadai:

if(scrollLeft < oneRule.upperLimit)

dengan ketentuan bahwa kondisi didefinisikan dalam urutan menaik (pertama yang terendah 0 to 1000,, dan kemudian 1000 to 2000misalnya)


action=function1- bukankah seharusnya ini titik dua? ;-) - Anda juga dapat menolak ini hanya memiliki batas atas karena, karena proses eliminasi, Anda tidak dapat jatuh ke dalam dua kelompok - kecuali jika itu niat Anda (untuk memungkinkan beberapa tindakan).
Brad Christie

@Brad Christie Of Course
Nivas

@Brad, bukan itu bukan maksud saya, dan Anda benar, batas atas sudah cukup. Akan menambahkan itu sebagai pembaruan ...
Nivas

Saya menemukan ini ringkas dan bersih +1
pimvdb

3

Apa yang sebenarnya kamu lakukan //do stuff?

Anda mungkin dapat melakukan sesuatu seperti:

(scrollLeft < 1000) ? //do stuff
: (scrollLeft > 1000 && scrollLeft < 2000) ? //do stuff
: (scrollLeft > 2000) ? //do stuff
: //etc. 

3

Belum diuji dan tidak yakin apakah ini akan berhasil, tetapi mengapa tidak melakukan beberapa if statementssebelumnya, untuk mengatur variabel untuk switch statement.

var small, big;

if(scrollLeft < 1000){
    //add some token to the page
    //call it small
}


switch (//reference token/) {
  case (small):
   //do stuff
   break;
  case (big):
   //do stuff;
   break;
}

2

Ini adalah pilihan lain:

     switch (true) {
         case (value > 100):
             //do stuff
             break;
         case (value <= 100)&&(value > 75):
             //do stuff
             break;
         case (value < 50):
            //do stuff
             break;
     }

1

Memperbarui jawaban yang diterima (belum dapat berkomentar). Pada 1/12/16 menggunakan demo jsfiddle di chrome, switch-direct adalah solusi tercepat.

Hasil: Resolusi waktu: 1.33

   25ms "if-immediate" 150878146 
   29ms "if-indirect" 150878146
   24ms "switch-immediate" 150878146
   128ms "switch-range" 150878146
   45ms "switch-range2" 150878146
   47ms "switch-indirect-array" 150878146
   43ms "array-linear-switch" 150878146
   72ms "array-binary-switch" 150878146

Jadi

 1.04 (   25ms) if-immediate
 1.21 (   29ms) if-indirect
 1.00 (   24ms) switch-immediate
 5.33 (  128ms) switch-range
 1.88 (   45ms) switch-range2
 1.96 (   47ms) switch-indirect-array
 1.79 (   43ms) array-linear-switch
 3.00 (   72ms) array-binary-switch

itu benar-benar tergantung - 15ms "jika-langsung" 15ms "jika-tidak langsung" 15ms "beralih-langsung" 37ms "switch-range" 28ms "switch-range2" 35ms "switch-indirect-array" 29ms "array-linear-switch" 62ms "array-binary-switch" Selesai 1,00 (15ms) jika-segera 1,00 (15ms) jika-tidak langsung 1,00 (15ms) beralih-langsung 2,47 (37ms) switch-range 1,87 (28ms) switch-range2 2,3 (35ms) switch- tidak langsung-array 1.93 (29ms) array-linear-switch 4.13 (62ms) array-binary-switch chrome Versi 48.0.2564.109 (64-bit) mac os x 10.11.3
RenaissanceProgrammer

ATM Safari 9.X pada Mac OS x dan Safari ios 9.3, "jika langsung" adalah pemenang yang jelas
RenaissanceProgrammer

1
Perbedaan 1 ms terlalu sedikit untuk diperhatikan. Ini bervariasi lebih dari itu dari setiap uji coba. Intinya adalah: Gunakan gaya pengkodean yang masuk akal, dan jangan mencoba mengoptimalkan mikro.
beberapa

1

Dalam kasus saya (kode warna persentase, tidak ada kinerja kritis), saya segera menulis ini:

function findColor(progress) {
    const thresholds = [30, 60];
    const colors = ["#90B451", "#F9A92F", "#90B451"];

    return colors.find((col, index) => {
        return index >= thresholds.length || progress < thresholds[index];
    });
}

1

Saya benci menggunakan 30 jika pernyataan

Saya memiliki situasi yang sama belakangan ini, itulah cara saya menyelesaikannya:

sebelum:

if(wind_speed >= 18) {
    scale = 5;
} else if(wind_speed >= 12) {
    scale = 4;
} else if(wind_speed >= 9) {
    scale = 3;
} else if(wind_speed >= 6) {
    scale = 2;
} else if(wind_speed >= 4) {
    scale = 1;
}

setelah:

var scales = [[4, 1], [6, 2], [9, 3], [12, 4], [18, 5]];
scales.forEach(function(el){if(wind_speed > el[0]) scale = el[1]});

Dan jika Anda menetapkan "1, 2, 3, 4, 5" maka itu bisa menjadi lebih sederhana:

var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.