Переключение оператора для больше / меньше чем - PullRequest
196 голосов
/ 12 июля 2011

, поэтому я хочу использовать оператор switch следующим образом:

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

Теперь я знаю, что любое из этих утверждений (<1000) или (>1000 && <2000) не будет работать (очевидно, по разным причинам). То, что я спрашиваю, является наиболее эффективным способом сделать это. Я ненавижу использовать 30 if операторов, поэтому я бы предпочел использовать синтаксис переключателя. Что я могу сделать?

Ответы [ 10 ]

629 голосов
/ 04 сентября 2012

Когда я посмотрел на решения в других ответах, я увидел некоторые вещи, которые, как я знаю, плохо влияют на производительность.Я собирался поместить их в комментарий, но я подумал, что было бы лучше сравнить его и поделиться результатами.Вы можете проверить это самостоятельно .Ниже приведены мои результаты (ymmv), нормализованные после самой быстрой операции в каждом браузере (умножьте 1,0 раз на нормализованное значение, чтобы получить абсолютное время в мс).

                    Chrome  Firefox Opera   MSIE    Safari  Node
-------------------------------------------------------------------
1.0 time               37ms    73ms    68ms   184ms    73ms    21ms
if-immediate            1.0     1.0     1.0     2.6     1.0     1.0
if-indirect             1.2     1.8     3.3     3.8     2.6     1.0
switch-immediate        2.0     1.1     2.0     1.0     2.8     1.3
switch-range           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

Тест, выполненный в Windows 7 32bit сследующие версии: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.10,7 . Узел был запущен на 64-битном компьютере с Linux, поскольку разрешение таймера в Node.js для Windows составляло 10 мс вместо 1 мс.

if-немедленный

Это самый быстрый ввсе протестированные среды, кроме ... барабанная дробь MSIE!(Сюрприз Сюрприз).Это рекомендуемый способ его реализации.

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

if-косвенный

Это вариант switch-indirect-array, но с if -условиями вместо этого и работает намного быстрее, чем switch-indirect-array почти во всех протестированных средах.

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

switch-немедленный

Это довольно быстро во всех протестированных средах и на самом деле самый быстрый в MSIE.Это работает, когда вы можете сделать расчет, чтобы получить индекс.

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

диапазон переключения

Это примерно в 6-40 раз медленнее, чем самая быстрая во всех протестированных средах, за исключением Opera, где она занимает примерно полтора раза.Это медленно, потому что двигатель должен сравнить значение дважды для каждого случая.Удивительно, но для выполнения этого Chrome требуется почти 40 раз больше времени по сравнению с самой быстрой операцией в Chrome, а MSIE - всего в 6 раз дольше.Но фактическая разница во времени составляла только 74 мс в пользу MSIE при 1337 мс (!).

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

switch-range2

Это вариант switch-range, но с одним сравнением для каждого случая и, следовательно, более быстрым, но все еще очень медленным, за исключением Opera.Порядок оператора case важен, поскольку движок будет проверять каждый случай в порядке исходного кода 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-косвенный-массив

InВ этом варианте диапазоны хранятся в массиве.Это медленно во всех протестированных средах и очень медленно в 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

Это комбинация линейного поиска значений в массиве и переключателязаявление с фиксированными значениями.Причина, по которой можно использовать это, заключается в том, что значения неизвестны до времени выполнения.Он медленный в каждой тестируемой среде и занимает почти в 10 раз больше времени в 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

Это вариант array-linear-switch, но с двоичнымпоиск.К сожалению, это медленнее, чем линейный поиск.Я не знаю, является ли это моей реализацией или линейный поиск более оптимизирован.Возможно также, что пространство клавиш слишком маленькое.

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

Заключение

Если важна производительность, используйте if -статизмы или switch с непосредственными значениями.

87 голосов
/ 12 июля 2011

Альтернатива:

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

Демо: http://jsfiddle.net/UWYzr/

20 голосов
/ 12 июля 2011
switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Работает только при регулярных шагах ...

РЕДАКТИРОВАТЬ: так как это решение продолжает получать голоса, я должен сообщить, что решение Мофоло намного лучше

6 голосов
/ 12 июля 2011

Вы можете создать пользовательский объект с критериями и функцией, соответствующей критериям

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

Определить функции для того, что вы хотите сделать в этих случаях (определить function1, function2 и т. Д.)

И "оценить" правила

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

Примечание

Я ненавижу использовать 30, если утверждения

Много раз, если утверждения легче читать и поддерживать.Я бы порекомендовал вышеупомянутое только тогда, когда у вас есть много условий и с возможностью роста много в будущем.

Обновление
Как указывалось в комментариях @Brad, если условия являются взаимоисключающими (только одно из них может быть истинным одновременно),проверка верхнего предела должна быть достаточной:

if(scrollLeft < oneRule.upperLimit)

при условии , что условия определены в порядке возрастания (сначала самый низкий, 0 to 1000, а затем 1000 to 2000, например)

3 голосов
/ 12 июля 2011

Не проверено и не уверены, сработает ли это, но почему бы не сделать несколько if statements ранее, чтобы установить переменные для 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;
}
3 голосов
/ 12 июля 2011

Что именно вы делаете в //do stuff?

Вы можете сделать что-то вроде:

(scrollLeft < 1000) ? //do stuff
: (scrollLeft > 1000 && scrollLeft < 2000) ? //do stuff
: (scrollLeft > 2000) ? //do stuff
: //etc. 
1 голос
/ 18 марта 2018

Я ненавижу использовать 30, если заявления

В последнее время у меня была такая же ситуация, вот как я ее решил:

до:

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

после:

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

И если вы установите «1, 2, 3, 4, 5», то это может быть еще проще:

var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
1 голос
/ 28 сентября 2016

В моем случае (цветовое кодирование в процентах, ничего не критично к производительности) я быстро написал следующее:

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 голос
/ 13 января 2016

Обновление принятого ответа (пока не могу комментировать).Начиная с 1/12/16 с использованием демонстрационной jsfiddle в Chrome, Switch-немедленное является самым быстрым решением.

Результаты: Разрешение по времени: 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

Завершено

 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
1 голос
/ 19 июля 2012

Это еще один вариант:

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