【问题标题】:Generating Nested Loops at Run Time in C [closed]在 C 中运行时生成嵌套循环 [关闭]
【发布时间】:2020-06-27 17:50:52
【问题描述】:

我知道使用递归方式,但它只适用于开始和结束语句与下面的代码相同的循环

for (int i=1 ; i<5;i++){
    for (int j=0; j<5;j++){
       for (int m= 0;m<5;m++) {
           // N time
       }
    }
}

但我的问题不同。我不知道编译时的 N 级和开始和结束语句。我会在运行时向用户学习。

喜欢:

for (int i=1 ; i<5;i++){
        for (int j=5; j<18;j++){
           for (int m= 4;m<5;m++) {
               // N time
           }
        }
    }

所以我的问题是:有没有办法在编译时不知道 N 的情况下创建嵌套的 N 级 for 循环?

【问题讨论】:

  • 如果您添加一些应该在循环内的示例,将会有所帮助。像这样的问题往往会寻求solution to the wrong problem
  • 您想要解决的真正问题是什么?为什么你认为循环嵌套是这个未知问题的正确(或唯一)解决方案?
  • 我知道使用递归方式,但它只适用于开始和结束条件与下面的代码相同的循环 - 这是什么意思?你的两个例子有什么不同?
  • 我想你的意思是N 嵌套级别。
  • 你可以编写一个程序,它的输出是一个包含N层嵌套的for循环的程序的源代码a。这不是很困难。但可能第一条评论仍然适用。如果您不告诉我们您实际上想要达到什么目标,则很难提供帮助。你可以开始告诉我们循环里面会发生什么。

标签: c loops for-loop while-loop nested


【解决方案1】:

您可以通过构造一些数据来充当迭代器并将其视为被迭代的单个事物来实现此目的。该数据将为您希望迭代的每个维度包含一个单独的计数器,并且它将由自身迭代每个维度的函数进行初始化、测试和递增。这是一个使用简单数组作为计数器的示例。

#include <string.h>


//  Initialize counters to their start values.
static void InitializeCounters(long N, long *Counters, const long *Starts)
{
    memcpy(Counters, Starts, N * sizeof *Counters);
}


//  Return 1 if there are more values to iterate, 0 otherwise.
static int MoreToIterate(long N, long *Counters, const long *Ends)
{
    return Counters[0] < Ends[0];
}


//  Increment the counters, lexicographic (dictionary/odometer) style.
static void IncrementCounters(long N, long *Counters, const long *Starts,
    const long *Ends)
{
    /*  Increment each dimension (except the first will be special).  If it
        rolls over its end, reset it to its start and go on the next dimension.
        If it does not roll over, stop there.
    */
    for (long i = N-1; 0 < i; --i)
        if (++Counters[i] < Ends[i])
            return;
        else
            Counters[i] = Starts[i];

    /*  For dimension zero, do not reset it, so MoreToIterate can see it
        finished.
    */
    ++Counters[0];
}


#include <stdio.h>
#include <stdlib.h>


static void _Noreturn Usage(char *argv[])
{
    fprintf(stderr, "Usage: %s <N>\n", argv[0]);
    exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
    if (argc != 2)
        Usage(argv);

    char *end;
    long N = strtol(argv[1], &end, 0);
    if (*end != '\0')
        Usage(argv);

    if (N < 0)
        Usage(argv);

    long *Counters = malloc(N * sizeof *Counters);
    long *Starts   = malloc(N * sizeof *Starts);
    long *Ends     = malloc(N * sizeof *Ends);
    if (!Counters || !Starts || !Ends)
    {
        fprintf(stderr, "Error, unable to allocate memory.\n");
        exit(EXIT_FAILURE);
    }

    //  Initialize start and end values as desired.
    for (long i = 0; i < N; ++i)
    {
        Starts[i] = 0;
        Ends[i]   = i+1;
    }

    for (   InitializeCounters(N, Counters, Starts);
            MoreToIterate(N, Counters, Ends);
            IncrementCounters(N, Counters, Starts, Ends))
    {
        for (long i = 0; i < N; ++i)
            printf("%ld ", Counters[i]);
        printf("\n");
    }

    free(Ends);
    free(Starts);
    free(Counters);
}

使用参数“3”执行时的示例输出为:

0 0 0 0 0 1 0 0 2 0 1 0 0 1 1 0 1 2

【讨论】:

  • 不错的答案,但希望不会超出 OP 的范围。
【解决方案2】:

这是一个更简单的版本,OP 和其他读者可能会觉得更容易理解。它需要适应使用运行时深度和数据,但它提供了如何进行这种多维迭代的意义。

#include <stdio.h>

#define DEPTH 3

void do_whatever(int counter[DEPTH]);

int main()
{
    int start[DEPTH] = {1, 0, 0};
    int limit[DEPTH] = {3, 6, 4};
    int counter[DEPTH];

    for (int i = 0; i < DEPTH; ++i)
        counter[i] = start[i];

    // Anything to do before the iteration

    while (1)
    {
        do_whatever(counter);

        // Increment the state
        int i = DEPTH - 1;
        while (i >= 0) {
            if (++counter[i] < limit[i])
                break;
            counter[i] = start[i];
            --i;
        }
        if (i >= 0)
            continue;
        break;
    }

    // Anything to do after the iteration

    return 0;
}


void do_whatever(int counter[DEPTH])
{
    printf("%2d %2d %2d\n", counter[0], counter[1], counter[2]);
}

【讨论】:

    【解决方案3】:

    所以基本上你想生成一个具有预定义范围的n 数字序列。

    只需增加第一个数字,遍历数字以查看它们是否达到最大值,查看数字是否等于最大值,如果是,则重置数字,增加下一个数字并重复。像这样:

    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <assert.h>
    #include <stdbool.h>
    
    /**
     * Generate a sequence of n numbers
     * each number between <ranges[i][0], ranges[i][1])
     * For each generated sequence call callback cb.
     * @param n - count of numbers
     * @param ranges - min and max range of each number position
     * @param cb - callback to call
     * @param cookie - cookie to pass to callback
     * @return - 0 on success and -ENOMEM on failure
     */
    int gen(size_t n,
            const int (*ranges)[2],
            void (*cb)(int *arr, size_t n, void *cookie),
            void *cookie) {
        if (n == 0) {
            return 0;
        }
    
        int *arr = calloc(n, sizeof(*arr));
        if (arr == NULL) { 
             return -ENOMEM;
        }
    
        // initialize array with min values
        for (size_t i = 0; i < n; ++i) {
            arr[i] = ranges[i][0];
        }
    
        while (7) {
    
            // call the callback
            cb(arr, n, cookie);
    
            // increment numbers in the array
            size_t i = 0;
            for (i = 0; i < n; ++i) {
                // we index array from the back
                const size_t idx = n - i - 1;
                // increment this position
                arr[idx]++;
                // if the numbers did not reach it's max
                if (arr[idx] < ranges[idx][1]) {
                    break;
                }
                // "reset" this position
                arr[idx] = ranges[idx][0];
            }
            // we went through all the numbers
            if (i == n) {
                break;
            }
        }
    
        free(arr);
        return 0;
    }
    
    void print_them(int *arr, size_t n, void *ignored) {
       for (size_t i = 0; i < n; ++i) {
            printf("%2d%c", arr[i], i + 1 == n ? '\n' : ' ');
       }
    }
    
    int main() {
        // do:
        // for (int i = 1; i < 5; ++i)
        //    for (int j = 5; j < 18; ++j)
        //        for (int k = 0; k < 5; ++k)
        return gen(3, (int[][2]){{1,5},{5,18},{0,5}}, print_them, NULL);
    }
    

    输出:

     1  5  0
     1  5  1
     1  5  2
     1  5  3
     1  5  4
     1  6  0
    ... many lines ... ca. (5-1)*(18-5)*(5-0) lines ...
     4 16  4
     4 17  0
     4 17  1
     4 17  2
     4 17  3
     4 17  4
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-07
      • 2021-11-04
      • 2016-11-18
      • 2014-12-14
      相关资源
      最近更新 更多