问题
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