如何为嵌套循环设置变量



我想写一个函数,它接受函数将执行的嵌套循环数作为输入。例如,如果输入参数为3,则函数将执行3个嵌套循环,如下所示:

for i=0:[any]
 for j=0:[any]
  for k=0:[any]

如果输入参数为2,则如下所示:

for i=0:[any]
 for j=0:[any]

如何实现此算法?

如上所述,这个问题通常可以通过递归来解决。

不知怎么的:

function res = nestedloop_function(numLoops,remainingParams)
    res = rec_function(numLoops,[],remainingParams);
end
function res = rec_function(numLoops, loopVars, remainingParams)
    if numLoops == 0
        res = whatever(loopVars, remainingParams);
        return res;
    end
    for k = 1:[any]
        loopVars = [loopVars,k];
        res = rec_function(numLoops-1, loopVars, remainingParams);
    end
end

如果你不想有传递remainingParamsloopVars的开销,你可以考虑将它们声明为global,但通常最好避免这种情况。。。

您可以将所有循环"打包"到一个循环中。以下代码假设

  • 所有循环的上限是相同的
  • 你只想做最内层的事情

Matlab代码:

N = 3; %// number of loops
M = 10; %// range for each variable is from 0 to M-1
for generalCounter = 0:M^N-1
   counters = dec2base(generalCounter,M,N)-'0';
   %// Now you are at the innermost "loop". counters(1) is your "i",
   %// counters(2) is your "j" etc
end

关键是使用一个通用计数器,并由此计算计数器ij等。这是用dec2base(...)-'0'在Matlab中完成的。-'0'部分是必要的,因为dec2base返回一个char数组,所以-'0'将每个char转换为它所代表的数字。在其他语言中,可能会有不同的做法,但你会明白的。

这可能对您有用。

不过也有一些假设。1.你只能在最里面的循环中执行一些东西。2.所有嵌套循环具有相同的上限

void loop(int n)
{
    if(n < 1)
    {
        return;
    }
    int i;
    loop(--n);
    for( i=0; i<2; i++)
    {
        printf("%d", i); //consider this portion to be executing inside the inner most loop 
    }
    }
int main()
{
    int x;
    int n = 5; //number of for loops you wanted nested
    loop(n);
}

您可以使用一个平面循环,并在一个被视为里程表的数组中跟踪循环变量:通过增加最内部的变量来推进循环,根据需要重置并转移到下一个外部变量。当进位超过嵌套的pasudo循环数时,停止循环。

这里有一个C语言的解决方案,它有一个无限平坦的循环,使用辅助函数来推进循环变量并测试里程表溢出:

#include <stdlib.h>
#include <stdio.h>
void odo_init(int ix[], int n)
{
    while (n--) ix[n] = 0;
}
int odo(int ix[], int m, int n)
{
    int i = 0;
    do {
        ix[i]++;
        if(ix[i] < m) return 1;
        ix[i++] = 0;
    } while (i < n);
    return 0;
}
int main()
{
    int m = 2;
    int n = 4;
    int ix[n];
    odo_init(ix, n);
    do {
        int i;
        /* Do something, e.g. print loop vars */
        for (i = 0; i < n; i++) {
            if (i) printf(", ");
            printf("%d", ix[i]);
        }
        printf("n");
        /* Advance and test loop variables */
    } while(odo(ix, m, n));
    return 0;
}

(函数odo_init是不必要的,因为可变长度数组不能用ix[n] = {0}初始化。)


如果你不介意C预处理器宏的晦涩,你可以使用这个框架来编写一个multi_for:

#define multi_for(ix, m, n)                 
    for (int ix[n], ix##_cnt_ = 0;          
        !(ix##_cnt_ || odo_init(ix, n));    
        ix##_cnt_++)                        
        for (int ix##_aux_ = 1;             
            ix##_aux_;                      
            ix##_aux_ = odo(ix, m, n))

无可否认,这个宏观政策很笨拙。它通过标记粘贴创建了局部循环变量数组ix和两个隐藏变量。外部for循环正好用于正确初始化循环变量数组。do。。。while已被重写为for,因此您可以像调用循环的正则函数一样调用宏:

int main()
{    
    int N = 4;
    multi_for(ix, 2, N) {
        int i;
        /* Do something, e.g. print loop vars */
        for (i = 0; i < N; i++) {
            if (i) printf(", ");
            printf("%d", ix[i]);
        }
        printf("n");
    }
    return 0;
}

要使其工作,必须更改odo_init以返回0:

int odo_init(int ix[], int n)
{
    while (n--) ix[n] = 0;
    return 0;
}

该宏依赖于在for内部定义变量和可变长度数组,因此需要C99。

用递归实现并不太困难,尽管它看起来仍然是一个奇怪的用例
无论如何,这使代码尽可能地易于理解,同时具有"无限"可扩展性*
每个循环也有自己的功能(尽管是相同的),正如您在对其他答案的注释中所指定的那样。

void do_loop(std::vector<int> *loop_vars, int loop_level, int max_loop_level, int loop_iter_limit, void* data)
{
  while(loop_vars[loop_level] < loop_iter_limit)
  {
    //do work on *data
    *loop_vars[loop_level]++;
    do_loop(loop_vars, loop_level+1, max_loop_level, loop_iter_limit, data);
  }
  else
  {
    return;
  }
}
void nestedloops(int loop_count, loop_limit, void* data)
{
  std::vector<int> vars;
  for (int i = 0; i<loop_count; i++)
    vars.pushback(0);
  do_loop(&vars, 0, loop_count, loop_limit, data);
  return;
}

例如用nestedloops(5,10,data);调用以执行5个嵌套循环,每个循环10次迭代。

您将想要将void* data更改为更合适的内容(我不知道您将要做什么工作)。当操作数据时,循环变量(i,j,k...)由矢量提供,因此不是:

for (int i = 0; i<max; i++)
{
  for (int j = 0; j<max; j++)
  {
    x += i+j;
  }
}

您将用替换工作(在本例中为x=i+j

*data += loop_vars[0]+loop_vars[1];

void* data将改为int* data

而不是循环,你可以调用

nestedloops(2,max,&x);

如果您需要澄清,请发表评论。

*不确定编译器是否能够对此执行tail call elimination,如果不能,则受堆栈深度的限制。然而,我认为500(GCC默认值)已经足够开始嵌套循环了,而且它可以配置得更高,但我也不知道你的用例,在这之前运行时间会变得非常可怕。

您可以使用meshgrid生成所有组合。

例如,考虑一个长度未知的向量limits,其中每个循环都应该执行for idx_k = 1:limit(k)。然后

 function unknownNested( limts )
 n = numel(limits); %// how many neted loops there are
 range = cell(1,n); %// range for each loop
 for ii=1:n
     range(ii) = 1:limits(ii);
 end      
 [idx{1:n}] = meshgrid( range{:} );
 idx = cellfun( @(x) x(:), idx, 'uni', false ); %// "flatten" all ranges
 allRanges = [idx{:}];
 %// now you can loop once over all combinations
 for ii = 1:size(allRanes,1)
     counters = allRanges(ii,:); %// now you have an n-vector with the counter of each nested loop
     %// do you work here...
 end         

您已经用Mathlab和C++标记了您的问题。你需要哪一个?这是用C++编写的。

如果我正确理解你的问题(n个循环,上限为m),那么你需要这样的东西:

void func(int countLoops, int loopUpperLimit) {
    // simpler code with 2 loops
    while (countLoops-- > 0) {
        for (int i = 0; i < loopUpperLimit; i++) {
            // do something
        }
    }
    // shorter code with one loop
    for (int i = 0, limit = loopUpperLimit * countLoops; i < limit; i++) {
        // do something
    }
}

上面的代码假设您不需要单独的控制变量(i、j、k等等)。

最新更新