1. ホーム
  2. ジャバスクリプト

[解決済み] 大なり小なりのSwitchステートメント

2022-03-03 18:59:19

質問

ということで、このようなswitch文を使いたいのですが。

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

これで、これらの文のどちらか( <1000 ) または ( >1000 && <2000 は動作しません(明らかに理由は違いますが)。 私が尋ねているのは、それを行うための最も効率的な方法です。 私は、30個の if ステートメントを使用したいので、switch構文を使用したいです。 何かいい方法はないでしょうか?

どのように解決するのですか?

他の回答で解決策を見たとき、パフォーマンスに悪いとわかっていることがいくつかありました。コメントに書こうと思ったのですが、ベンチマークして結果を共有した方が良いと思いました。あなたは 自分でテストする . 以下は、各ブラウザで最速の操作を行った後に正規化した私の結果です(ymmv)。

以下は、2021-MAY-05の結果です。

<テーブル テスト クローム ファイアフォックス オペラ エッジ ブレイブ ノード 1.0倍 15ミリ秒 14ミリ秒 17ミリ秒 17ミリ秒 16ミリ秒 14ミリ秒 if-immediate 1.00 1.00 1.00 1.00 1.00 1.00 if-indirect 2.20 1.21 2.06 2.18 2.19 1.93 スイッチ・イミディエイト 2.07 1.43 1.71 1.71 2.19 1.93 スイッチ範囲 3.60 2.00 2.47 2.65 2.88 2.86 スイッチ範囲2 2.07 1.36 1.82 1.71 1.94 1.79 switch-indirect-array 2.93 1.57 2.53 2.47 2.75 2.50 アレイリニアスイッチ 2.73 3.29 2.12 2.12 2.38 2.50 アレイバイナスイッチ 5.80 6.07 5.24 5.24 5.44 5.37

2021年のテストは、Windows 10 64bitで、以下のバージョンで実施しました。 Chrome 90.0.4430.212 , ファイアフォックス 89.0b13 , オペラ 76.0.4017.123 , エッジ 90.0.818.62 , ブレイブ 1.24.85 そして ノード 16.1.0 (WSLで実行されました)

Appleはアップデートしない Windows版Safari ということで、5.1.7のままです。今回のテストではBraveに変更しました。

こちらは過去比較のため、2012-09-04の結果です。

<テーブル テスト クローム ファイアフォックス オペラ MSIE サファリ ノード 1.0倍 37ミリ秒 73ミリ秒 68ミリ秒 184ミリ秒 73ミリ秒 21ミリ秒 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 スイッチ・イミディエイト 2.0 1.1 2.0 1.0 2.8 1.3 スイッチ範囲 38.1 10.6 2.6 7.3 20.9 10.4 スイッチ・レンジ2 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 アレイリニアスイッチ 3.6 4.1 4.5 10.0 4.7 2.7 アレイバイナスイッチ 7.8 6.7 9.5 16.0 15.0 4.9

2012年のテストは、Windows 7 32bitで、以下のバージョンで実施しました。 クローム 21.0.1180.89m , Firefox 15.0 , オペラ 12.02 , MSIE 9.0.8112 , サファリ 5.1.7 . ノード は、Windows版Nodeのタイマーの解像度が1msではなく10msだったため、Linuxの64bitボックスで実行されました。

if-immediate(イフ・イミディエイト

この方法は、...を除くすべてのテストされた環境において最も速い方法です。 ドラムロール MSIE! (驚き、驚き)。

これが推奨される実装方法です。

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

if-indirect

のバリエーションです。 switch-indirect-array しかし if -ステートメントの代わりに、すべてのテストされたエンジンでより速いです。

2021年では最速のテストより20~120%(2012年:0~280%)遅くなっています。Chromeは2012年(1.2)よりも2021年(2.20)の方が時間がかかっている

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

スイッチ・イミディエイト

これは、計算をしてインデックスを得ることができる場合に有効です。

2021年には40~120%(2012年:0~180%)遅くなっています。 if-immediate ただし、MSIEでは最速でした。

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

スイッチ範囲

エンジンはそれぞれのケースで2回値を比較しなければならないので、遅くなります。

2021年のテストでは、最速の1~2.6倍(2012年:1.6~38倍)遅くなっています。 Chromeは38から3.6と最も大きな改善を遂げたが、依然としてテストされたエンジンの中で最も遅い。

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

スイッチ・レンジ2

のバリエーションです。 switch-range が、1つのケースにつき1つの比較しかしないので、より高速になります。 エンジンは各ケースをソースコード順にテストするので、case 文の順序は重要です。 ECMAScript 2020 13.12.9

2021年のテストでは最速で36~107%遅かったが、2012年は1~31倍遅くなっている。このテストで最もパフォーマンスが悪いのは依然としてChromeですが、32倍から2倍に改善されています。

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

スイッチ・インダイレクト・アレイ

このバリエーションでは、範囲は配列に格納されます。

2021年のテストでは、最速で57~193%(2012年:3~35倍)遅くなっています。 テストしたすべてのエンジンでペルバランスが改善され、Chromeはまだ最も遅いものの、35から2.93に改善されました。

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

アレイリニアサーチ

このバリエーションでは、範囲は配列に格納されます。

2021年のテストでは、最速で57~193%(2012年:3~35倍)遅くなっています。 テストしたすべてのエンジンでペルバランスが改善され、Chromeはまだ最も遅いものの、35から2.93に改善されました。

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-linear-switch が、バイナリサーチで 残念ながら、線形探索より遅いです。私の実装のせいなのか、線形探索の方が最適化されているのかは分かりません。また、キースペースが小さすぎることも原因かもしれません。

2021年では4-5倍(2012年:4-16倍)遅くなっています。 を使用しないでください。 .

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 を、即値で表示する。