编辑:对不起,但我忘了提到我需要计数器变量的值。所以制作一个循环不是我害怕的解决方案。
我不确定这是否可行,但我想做以下事情。
对于函数,传递数字数组。每个数字都是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
不应该是首选,因为它也会产生危险的结果。
这里适合哪种技术?
答案 0 :(得分:19)
递归可以很好地解决这个问题:
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);
答案 1 :(得分:9)
递归在这里是过度的。更快的解决方案:
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); })
答案 2 :(得分: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);
}
答案 3 :(得分: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 */ });
答案 4 :(得分:2)
一种解决方案,无需以编程方式进行复杂操作,即采用整数并将它们全部乘以。由于您只是嵌套ifs,并且只有最里面的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();
}
}
答案 5 :(得分:1)
这是我尝试简化非递归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);
});
答案 6 :(得分:0)
做三个循环的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);
答案 7 :(得分: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
答案 8 :(得分:0)
也可以为此使用生成器:
app.intent('Location', (conv, {'geo-city': geoCity}) => {
const luckyNumber = geoCity.length;
// ...
});
然后可以用作:
function loop(...times) {
function* looper(times, prev = []) {
if(!times.length) {
yield prev;
return;
}
const [max, ...rest] = times;
for(let current = 0; current < max; current++) {
yield* looper(rest, [...prev, current]);
}
}
return looper(times);
}