Εργαστήριο Τεχνολογίας Λογισμικού
+3 votes
527 views

Πώς θα μπορούσα να έχω μεταβλητό πλήθος Ν εμφωλευμένων βρόχων, άγνωστο κατά τον χρόνο μεταγλώττισης;
Για παράδειγμα για Ν=3 επιθυμώ:

for (int i=0; i<someNumber; i++) {
    for (int j=0; j<someNumber; j++) {
        for (int k=0; k<someNumber; k++) {
            doSomething(i,j,k);
        }
    }
}
in progintro by (2.9k points) | 527 views
+2

Καταρχάς να σου πω ότι αυτό που ζητάς είναι μάλλον περίεργο και πιθανότατα υπάρχει καλύτερη/άλλη λύση στο πρόβλημά σου. Επίσης είναι πολύ πιθανό να σε οδηγήσει σε κάτι που τρέχει πολλή ώρα (αν για παράδειγμα το doSomething παιρνει 1 μονάδα χρόνου τότε το όλο task θα πάρει someNumber^N χρόνο).

Αν παρ' όλα αυτά επιμένεις υπάρχει κάτι αντίστοιχο εδώ.
Αν αποφασίσεις πως κάτι τέτοιο θέλεις (δεν θα έπρεπε), έχω έτοιμη μια προσαρμογή του παραπάνω κώδικα στη μορφή που το ζήτησες.

+2

Νομίζω πρέπει να σκεφτείς ο ίδιος

  • Σίγουρα χρειάζεσαι κάτι τέτοιο?
  • Αν ναι, πρέπει να σκεφτείς το pattern με το οποίο "μεταβλητά" θα πραγματοποιείς τις επαναλήψεις

Συγκεκριμένα για το παραπάνω παράδειγμα που ανέφερες θα είχα περισσότερες ερωτήσεις του τύπου:

  • Θες να έχουν όλες οι λούπες τον ίδιο αριθμό επαναλήψεων?
  • Χρειάζεσαι τους μετρητές της κάθε μίας απο αυτές (τα αντίστοιχα i/j/k που χρησιμοποιείς στο εσωτερικό)

Χωρίς να είμαι σε θέση να απαντήσω στην ερώτησή σου νομίζω αυτό που ψάχνεις μπορεί να γίνει με αναδρομική λύση.

+1

Δεν θέλω να γράψω λύση γιατί ίσως αποπειραθείς να την χρησιμοποιήσεις.
Αλλα για τα πρακτικά, αν γνωρίζεις το μέγεθος που θες κατα την διάρκεια της μεταγλώττισης μπορεί να γίνει με templates.
Ή αν είναι κατα το runtime,ίσως, με κάποια συνάρτηση που καλεί lambda's.

0

Ευχαριστώ για τις απαντήσεις. Όντως γνωρίζω ότι η λύση αυτή γενικά είναι (υπερβολικά) μη αποδοτική. Όμως είναι ενδιαφέρον γενικά ως concept το πώς γίνεται κάτι τέτοιο.

2 Answers

+2 votes
Best answer

Δεν μπορείς. Τουλάχιστον όχι στις φυσιολογικές γλώσσες προγραμματισμού. Προτείνω να χρησιμοποιήσεις κάποιο while loop που να προσομοιώνει τα πολλά for loops που χρειάζεσαι.


Για όποιον θέλει να καεί και σίγουρα όχι για το progintro: Αυτό που ζητάει ο ερωτών μπορεί να προσομοιωθεί με ένα for loop όπου η μεταβλητή είναι ένα vector. Στη C++ μπορείτε να το σχεδιάσετε και να το υλοποίησετε έτσι ώστε να γίνει αρκετά κομψό.

by (9.5k points)
selected by
0

Στο vector υποθέτω θα έκανες το παραμετρικό push των counters? :P

+2

Εγώ σκέφτηκα περίπου κάτι τέτοιο, αν κι όχι πλήρως λειτουργικό, είναι μια βασική ιδέα

http://cpp.sh/6yvn6

#include <iostream>
#include <vector>
#include <functional>
#include <iterator>


struct ForProtoType {
    int start;
    std::function<bool( int x)> condition;
    std::function<int(int x)> step;
    // maybe another lambda for operations not in final loop ???
};

class NestedFor {
    public:
        NestedFor() = default; 
        
        template< class... Args >
        void emplace_back(Args&&... args ){ loops.emplace_back(args...);}
        
        void exec() {
            auto l = loops.begin();
            std::vector<int> nums;
            exec_(l, nums);
        }

    private:
        std::vector<ForProtoType> loops;
        
        void exec_(std::vector<ForProtoType>::iterator l, std::vector<int>& nums ) {
            if (l == loops.end()){  // user should pass a function to do that probably
                for (auto& n: nums){
                    std::cout << n << " ";
                }
                std::cout << "\n";
            }
            else {
                for (int i= (*l).start; (*l).condition(i); i=(*l).step(i)){
                    nums.push_back(i);
                    exec_(l+1, nums);
                    nums.pop_back();
                }
            }
        }
};



int main() {  
    NestedFor thisIsBad;
    
    thisIsBad.emplace_back(
        (ForProtoType){1, 
        [](const int x){return x <= 20;}, 
        [](const int x){return x+1;}
    });
    
    thisIsBad.emplace_back(
        (ForProtoType){1, 
        [](const int x){return x <= 20;}, 
        [](const int x){return x+2;}
    });
    
    
    thisIsBad.exec();
    
}
0

Νομίζω κατάλαβα πώς το εννοείτε. Το παρακάτω νομίζω υλοποιεί αυτό που προτείνετε (το counter[0] είναι ο μετρητής του πιο εσωτερικού βρόχου και το counter[N-1] είναι ο μετρητής του πιο εξωτερικού βρόχου):

void variableNestedLoops(int n, int startingValue, int maxValue) {
    int *counter, cur;
    bool notFinished = true;

    counter = new int[n];

    for (int i=0; i<n; i++) counter[i] = startingValue;

    while (notFinished) {
            doSomething(counter);

            counter[0]++;
            cur = 0;
            while (counter[cur] == maxValue) {
                    if (cur == n-1) {
                            notFinished = false;
                            break;
                    }
                    counter[cur] = startingValue;
                    cur++;
                    counter[cur]++;
            }
    }
    delete[] counter;
}
0 votes

Ισώς να κανεις μια συναρτηση με for loop που θα κανει αναδρομη (recursion)?
(και με vectors που είναι ενα είδος array με μεταβλητό μέγεθος:)

// πρέπει να συμπεριλάβεις τη βιβλιοθήκη 
#include <vector>
#include <iostream>

// Απλά μια συναρτηση που δέχεται τους i,j,k... και ο αριθμός των επαναλήψεων κάθε for
void doSomething(std::vector<int> indexes) 
{
     // πχ τύπωσε κάθε index: i,j,k,...
    for (int i = 0; i < indexes.size(); i++) 
    {
         std::cout << indexes[i] << ' ';
    }
    std::cout << std::endl;
}
int someNumber = 9;

// η συνάρτηση που θα κανει for loops και αναδρομή (το super στο όνομα προέρχεται από τη super power ^^ που ο ζάχος εξήγησε σε διάλεξη...
void superForLoop(int numberOfForLoopsToGo, std::vector<int> indexes) 
{
    // Αν έχουμε φθάσει στο numberOfForLoopsToGo = 0 τότε αρκεί να επιστρέψουμε την τιμή της doSomething:
    if ( numberOfForLoopsToGo == 0 ) doSomething(indexes);
    else { // Αλλιώς κάνε άλλο ενα loop:
        for (int i = 0; i < someNumber; i++) 
        {
            // αντέγραψε τη λιστα με τα indexes και πρόσθεσε τον νεο index για να τη παραδώσεις στην αναδρομή:
            std::vector<int> updatedIndexes = indexes;
            updatedIndexes.push_back( i );
            superForLoop( numberOfForLoopsToGo - 1, updatedIndexes);
        }
    }
}

int main() 
{
    superForLoop(3, std::vector<int> {}); // θα κάνει for loops με βαθος 5. Του δίνω μια αδεια vector γιατι αλλιως δεν....
}

To πρόγραμμα αυτο (δουλευει κανε copy paste) τωρα κάνει 3 nested for loops και το κάθε for loop εκτελείτε 10 φορές.
Αρα η doSomething που τυπώνει στο τέλος 3ου for τα indexes καθε for loop θα τυπώσει ουσιαστικα όλους τους αριθμούς απο 0 μέχρι και 999. tah dah

Ελπίζω να πάρω αρκετούς πόντους. μου πήρε πολύ ωρα να γράφω σε makrdown αυτο το προγραμμα...
επίσης είναι κακή ιδέα να χρησιμοποιήσεις κάτι τέτοιο μάλλον...

by (750 points)
edited by

301 questions

289 answers

288 comments

903 users