2011-01-13 124 views
18

编辑:我很抱歉,但我忘了提及我需要计数器变量的值。所以做一个循环并不是我害怕的解决方案。可变数量的嵌套循环

我不确定这是否可能,但我想要做以下事情。 函数传递一个数组数组。每个数字是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循环的量等于所述阵列的长度。有什么办法可以做到这一点?我正在考虑创建一段代码,将每个循环添加到一个字符串中,然后通过eval进行评估。不过,我读过eval不应该是首选,因为它也可能有危险的结果。

这里有什么技术可能适用?

+2

所以你只是想调用一些功能的次数等于数字的产品在传入的数组? – Sean 2011-01-13 18:13:47

+0

不,我很抱歉。我将需要for循环(a,b和c)的变量。 – pimvdb 2011-01-13 18:19:38

回答

17

递归可以解决这个问题整齐:

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

做3个2,3,5环和30个环(2 * 3 * 5)之间没有区别。

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); 
+1

我真的很抱歉,但我还需要计数器变量的值。虽然我喜欢你的解决方案,但是这些信息会丢失。 – pimvdb 2011-01-13 18:16:06

+0

你打败了我。另外,你需要什么样的信息呢?你可以简单地把所有的整数都放在你原有的数组中吗?还是你需要让他们以不同的方式举行? – user535617 2011-01-13 18:19:10

+0

我想创建一个通用的多维数组函数,所以我需要用值填充索引的每个组合。因此嵌套for循环。一个for循环会使索引丢失,并返回一个索引(此处为0 - 30) – pimvdb 2011-01-13 18:22:01

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”部分)。上面的主要功能在内部功能中完成所有的实际工作。在这个内部函数中,第一个参数是计数器限制列表,当函数被递归调用时,它将被“削减”。第二个参数用于保存当前计数器值集合,以便“doSomething”可以知道它是在一个对应于特定实际计数列表的迭代中。

调用函数应该是这样的:

forEachCounter([4, 2, 5], function(c) { /* something */ }); 
1

一个解决方案,如果没有越来越复杂编程方式是采取了整数和繁殖他们所有的作品。既然你只嵌套IFS,只有最里面的一个有功能的,这应该工作:

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

递归在这里是过度的。更快的溶液:

function allPossibleCombinations(lengths, fn) { 
 
    var n = lengths.length; 
 

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

 
    while (true) { 
 
    fn.apply(null, indices); 
 
    // Increment indices. 
 
    ++indices[n - 1]; 
 
    for (var j = n; --j >= 0 && indices[j] === lengths[j];) { 
 
     if (j === 0) { return; } 
 
     indices[j] = 0; 
 
     ++indices[j - 1]; 
 
    } 
 
    } 
 
} 
 

 
allPossibleCombinations([3, 2, 2], function(a, b, c) { console.log(a + ',' + b + ',' + c); })

0

可以使用贪婪算法来枚举笛卡尔积0的所有元素:2×0:3×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 
0

这是我在简化非递归solution by Mike Samuel尝试。我还添加了为每个整数参数设置一个范围(不仅仅是最大值)的功能。

function everyPermutation(args, fn) { 
 
    var indices = args.map(a => a.min); 
 

 
    for (var j = args.length; j >= 0;) { 
 
     fn.apply(null, indices); 
 

 
     // go through indices from right to left setting them to 0 
 
     for (j = args.length; j--;) { 
 
      // until we find the last index not at max which we increment 
 
      if (indices[j] < args[j].max) { 
 
       ++indices[j]; 
 
       break; 
 
      } 
 
      indices[j] = args[j].min; 
 
     } 
 
    } 
 
} 
 

 
everyPermutation([ 
 
    {min:4, max:6}, 
 
    {min:2, max:3}, 
 
    {min:0, max:1} 
 
], function(a, b, c) { 
 
    console.log(a + ',' + b + ',' + c); 
 
});