Generating Nested Loops at Run Time in C [closed]

余生颓废 提交于 2021-02-20 01:32:34

问题


I know using recursions way but it works just for loops which beginning and ending statements are same like code below

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

But my problem is different. I do not know N level and beginning and ending statements at compile time. I will learn them from user at Run Time.

Like:

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

So my question is : Is there a way to create nested N level for loop without knowing N at compile time?


回答1:


You can do this by constructing some data to act as an iterator and treating it as a single thing that is iterated. That data will contain a separate counter for each dimension you wish to iterate, and it will be initialized, tested, and incremented by functions that themselves iterate over each dimension. Here is an example using a simple array for the counters.

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

Sample output when executed with argument “3” is:

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



回答2:


Here is a simpler version that the OP and other readers may find easier to follow. It would need to be adapted to use runtime depth and data, but it gives the sense of how to do this sort of multi-dimensional iteration.

#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:


So basically you want to generate a sequence of n numbers with predefined range.

Just increment first number, iterate over the numbers to see if they reached max, see if the number is equal to it's max, if it is, reset the number, increment next number and repeat. Like this:

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

outputs:

 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


来源:https://stackoverflow.com/questions/60708989/generating-nested-loops-at-run-time-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!