27

編集:申し訳ありませんが、カウンター変数の値が必要になることを忘れていました。したがって、ループを1つ作成することは解決策ではありません。

これが可能かどうかはまったくわかりませんが、次のことを行いたいと思います。関数には、数値の配列が渡されます。各数値は for ループの上限です。たとえば、配列が の場合[2, 3, 5]、次のコードを実行する必要があります。

for(var a = 0; a < 2; a++) {
     for(var b = 0; b < 3; b++) {
          for(var c = 0; c < 5; c++) {
                doSomething([a, b, c]);
          }
     }
}

したがって、ネストされた for ループの量は、配列の長さと同じです。これを機能させる方法はありますか?それぞれの for ループを文字列に追加し、それを で評価するコードを作成することを考えていましたeval。私は読んだことevalがありますが、それは危険な結果をもたらす可能性があるため、最初の選択肢であってはなりません.

ここではどのようなテクニックが適切でしょうか?

4

9 に答える 9

23

再帰はこの問題をきれいに解決できます。

function callManyTimes(maxIndices, func) {
    doCallManyTimes(maxIndices, func, [], 0);
}

function doCallManyTimes(maxIndices, func, args, index) {
    if (maxIndices.length == 0) {
        func(args);
    } else {
        var rest = maxIndices.slice(1);
        for (args[index] = 0; args[index] < maxIndices[0]; ++args[index]) {
            doCallManyTimes(rest, func, args, index + 1);
        }
    }
}

次のように呼び出します。

callManyTimes([2,3,5], doSomething);
于 2011-01-13T18:29:05.507 に答える
10

ここでの再帰はやり過ぎです。ジェネレーターを使用できます:

function* allPossibleCombinations(lengths) {
  const n = lengths.length;

  let indices = [];
  for (let i = n; --i >= 0;) {
    if (lengths[i] === 0) { return; }
    if (lengths[i] !== (lengths[i] & 0x7fffffff)) { throw new Error(); }
    indices[i] = 0;
  }

  while (true) {
    yield indices;
    // Increment indices.
    ++indices[n - 1];
    for (let j = n; --j >= 0 && indices[j] === lengths[j];) {
      if (j === 0) { return; }
      indices[j] = 0;
      ++indices[j - 1];
    }
  }
}

for ([a, b, c] of allPossibleCombinations([3, 2, 2])) {
  console.log(`${a}, ${b}, ${c}`);
}

ここでの直感は、常に対応する長さよりも小さいインデックスのリストを保持しているということです。

2 番目のループはキャリーを処理します。10 進数の 199 をインクリメントするときと同様に、(1, 9, 10) に進み、次にキャリーして (1, 10, 0) を取得し、最後に (2, 0, 0) を取得します。桁数が足りない場合は、これで終わりです。

于 2011-01-14T18:14:30.993 に答える
3

制限配列と同じ長さのカウンターの配列を設定します。単一のループを使用し、各反復の最後の項目をインクリメントします。制限に達したら、再起動して次のアイテムをインクリメントします。

function loop(limits) {
  var cnt = new Array(limits.length);
  for (var i = 0; i < cnt.length; i++) cnt[i] = 0;
  var pos;
  do {
    doSomething(cnt);
    pos = cnt.length - 1;
    cnt[pos]++;
    while (pos >= 0 && cnt[pos] >= limits[pos]) {
      cnt[pos] = 0;
      pos--;
      if (pos >= 0) cnt[pos]++;
    }
  } while (pos >= 0);
}
于 2011-01-13T18:14:25.717 に答える
2

ネストされたforループの観点から考えるのではなく、再帰関数の呼び出しについて考えてください。反復を行うには、次の決定を行います(擬似コード)。

if the list of counters is empty
    then "doSomething()"
else
    for (counter = 0 to first counter limit in the list)
        recurse with the tail of the list

これは次のようになります。

function forEachCounter(counters, fn) {
  function impl(counters, curCount) {
    if (counters.length === 0)
      fn(curCount);
    else {
      var limit = counters[0];
      curCount.push(0);
      for (var i = 0; i < limit; ++i) {
        curCount[curCount.length - 1] = i;
        impl(counters.slice(1), curCount);
      }
      curCount.length--;
    }
  }
  impl(counters, []);
}

カウント制限のリストである引数と、各有効なカウント配列(「doSomething」部分)に対して実行する関数である引数を使用して関数を呼び出します。上記のmain関数は、内部関数で実際のすべての作業を実行します。その内部関数では、最初の引数はカウンター制限リストであり、関数が再帰的に呼び出されるときに「絞り込まれ」ます。2番目の引数は、現在のカウンター値のセットを保持するために使用されます。これにより、「doSomething」は、実際のカウントの特定のリストに対応する反復にあることを知ることができます。

関数の呼び出しは次のようになります。

forEachCounter([4, 2, 5], function(c) { /* something */ });
于 2011-01-13T18:15:42.757 に答える
2

プログラム的に複雑になることなく機能する1つの解決策は、整数を取り、それらすべてを乗算することです。ifをネストしているだけで、最も内側のifだけが機能を持っているので、これは機能するはずです。

var product = 0;
for(var i = 0; i < array.length; i++){
    product *= array[i];
}

for(var i = 0; i < product; i++){
    doSomething();
}

または:

for(var i = 0; i < array.length; i++){
    for(var j = 0; j < array[i]; j++){
        doSomething();
    }
}
于 2011-01-13T18:17:01.017 に答える
0

2、3、5の3つのループと30(2 * 3 * 5)の1つのループを実行することに違いはありません。

function doLots (howMany, what) {
    var amount = 0;

    // Aggregate amount
    for (var i=0; i<howMany.length;i++) {
        amount *= howMany[i];
    };

    // Execute that many times.    
    while(i--) {
        what();
    };
}

使用する:

doLots([2,3,5], doSomething);
于 2011-01-13T18:14:25.887 に答える
0

貪欲アルゴリズムを使用して、デカルト積 0:2 x 0:3 x 0:5 のすべての要素を列挙できます。このアルゴリズムは、greedy_backward以下の関数によって実行されます。私は Javascript の専門家ではないので、この機能は改善される可能性があります。

function greedy_backward(sizes, n) {
  for (var G = [1], i = 0; i<sizes.length; i++) G[i+1] = G[i] * sizes[i]; 
  if (n>=_.last(G)) throw new Error("n must be <" + _.last(G));
  for (i = 0; i<sizes.length; i++) if (sizes[i]!=parseInt(sizes[i]) || sizes[i]<1){  throw new Error("sizes must be a vector of integers be >1"); }; 
  for (var epsilon=[], i=0; i < sizes.length; i++) epsilon[i]=0;
  while(n > 0){
    var k = _.findIndex(G, function(x){ return n < x; }) - 1;
    var e = (n/G[k])>>0;
    epsilon[k] = e;
    n = n-e*G[k];
  }
  return epsilon;
}

反辞書編集順序でデカルト積の要素を列挙します (doSomething例で完全な列挙を確認できます)。

~ var sizes = [2, 3, 5];
~ greedy_backward(sizes,0);
0,0,0
~ greedy_backward(sizes,1);
1,0,0
~ greedy_backward(sizes,2);
0,1,0
~ greedy_backward(sizes,3);
1,1,0
~ greedy_backward(sizes,4);
0,2,0
~ greedy_backward(sizes,5);
1,2,0

これはバイナリ表現の一般化です ( の場合sizes=[2,2,2,...])。

例:

~ function doSomething(v){
    for (var message = v[0], i = 1; i<v.length; i++) message = message + '-' + v[i].toString(); 
    console.log(message); 
  }
~ doSomething(["a","b","c"])
a-b-c
~ for (var max = [1], i = 0; i<sizes.length; i++) max = max * sizes[i]; 
30
~ for(i=0; i<max; i++){
    doSomething(greedy_backward(sizes,i));
  }
0-0-0
1-0-0
0-1-0
1-1-0
0-2-0
1-2-0
0-0-1
1-0-1
0-1-1
1-1-1
0-2-1
1-2-1
0-0-2
1-0-2
0-1-2
1-1-2
0-2-2
1-2-2
0-0-3
1-0-3
0-1-3
1-1-3
0-2-3
1-2-3
0-0-4
1-0-4
0-1-4
1-1-4
0-2-4
1-2-4

必要に応じて、逆の操作は簡単です。

function greedy_forward(sizes, epsilon) {
  if (sizes.length!=epsilon.length) throw new Error("sizes and epsilon must have the same length");
  for (i = 0; i<sizes.length; i++) if (epsilon[i] <0 || epsilon[i] >= sizes[i]){  throw new Error("condition `0 <= epsilon[i] < sizes[i]` not fulfilled for all i"); }; 
  for (var G = [1], i = 0; i<sizes.length-1; i++) G[i+1] = G[i] * sizes[i]; 
  for (var n = 0, i = 0; i<sizes.length; i++)  n += G[i] * epsilon[i]; 
  return n;
}

例 :

~ epsilon = greedy_backward(sizes, 29)
1,2,4
~ greedy_forward(sizes, epsilon)
29
于 2015-04-07T10:01:01.027 に答える