在运行时在C中生成嵌套循环

问题描述 投票:1回答:3

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

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级?

c loops for-loop while-loop nested
3个回答
2
投票

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

#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 00 0 10 0 20 1 00 1 10 1 2

0
投票

这里是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]);
}

0
投票

因此,基本上,您希望生成具有预定义范围的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
© www.soinside.com 2019 - 2024. All rights reserved.