for-Schleifen zusammenfassen



  • Hallo,

    in meinem Programm habe ich folgendes:

    for (int i = 0; i < Count; i++)
    {
        for (int j = 0; j < Count; j++)
        {
            for (int k = 0; k < Count; k++)
            {
                for (int l = 0; l < Count; l++)
                {
                    for (int m = 0; m < Count; m++)
                    {
                    	//hier werden i,j,k,l,m benutzt
                    }
                }
            }
        }
    }
    

    Gibt es da keine Möglichkeit dies zusammen zu fassen?
    Eine große for-Schleife nützt mir ja nichts da ich i,j,k,l,m brauche.

    Danke im Voraus



  • schreib doch eine kleine rekursive Funktion, der du am Anfang i, j, k, l, m übergibst und die sich dann immerwieder selbst aufruft (mit erhöhten werten).Weist du wie ich das meine oder brauchst du ein Beispiel?



  • Hi,

    hab vielleicht vergessen zu sagen das die Anzahl der for-Schleifen individuell sein sollte.

    Danke im Voraus



  • Dann würde ich es erstrecht mit einer rekursiven Funktion machen, der du ein Array übergibst, in das so lang ist, wie du for Schleifen haben willst. Ausßerdem übergib am besten noch die Länge des Arrays.



  • Hi,

    mh da bräuchte ich dann glaub ich doch ein Beispiel?
    So was in der Art brauche ich, aber theoretisch auch für 100 Schleifen:

    for (int i = 0; i < Count; i++) 
    { 
        for (int j = 0; j < Count; j++) 
        { 
            for (int k = 0; k < Count; k++) 
            { 
                for (int l = 0; l < Count; l++) 
                { 
                    for (int m = 0; m < Count; m++) 
                    { 
                       printf ("%i%i%i%i%i",i,j,k,l,m);
                    } 
                } 
            } 
        } 
    }
    

    Danke im Voraus



  • man könnte auch sowas machen:

    const size_t number = 10;
    int counter[number+1];     //das array ist jetzt um 1 größer 
    // als nötig gemacht, um die endabfrage einfacher zu machen :rolling_eyes:
    zero( counter, number+1 ); //die funktion ist trivial...
    do
    {
      //mach was mit deinen counter[i]
      add1( counter, number+1 );
    }
    while( !counter[number] );
    
    // und add1 sieht so in der art aus:
    
    void add1( int *counter, int size )
    {
      int *end = counter + size-1;
      ++(*counter);
      while( counter != end )
      {
        if(*counter == Count )
        {
           *counter = 0;
           ++counter;
           ++(*counter);
        }
      }  
    }
    

    das ist jetzt ziemlich hässlich, und u.u. ist auch ein böser fehler drin, aber du verstehst, was ich meine? einfach einen array basteln, und eine funktion, die das array als zahl zur basis Count auffasst und immer eins addiert... im prinzip genau so schön/hässlich wie die rekursionsmethode, nur ohne die stackverschwendung 🤡.



  • Falls die Schleifen rel. wenig machen ist eine Rekusion nicht zu empfehlen, da es sonst saulahm wird und evtl. ein Stack-Overflow kommt.



  • Wieso kommt es zu einem Stack-Overflow, wenn die Funktion wenig macht?

    Sry, ich bin noch nicht so ganz fit in Sachen Speicher.



  • Für jeden Aufruf wird ein eigener Stackframe erstellt und des braucht Zeit und Platz. Sobald die Funktion sehr wenig macht, wird dieser Overhead schnell "spürbar", v.a. wenn man die Funktion dann auch noch ständig wieder aufruft und zum Stack overflow kommts evtl. weil, wenn eine Funktion sich ständig selber aufruft der Stackverbraucht immer größer wird.



  • Was meinst du mit "spürbar"? Das der Verbrauch extrem steigt? Aber würde er das nicht auch bei einer größeren Funktion (bzw sogar noch mehr? )



  • MaxDemian schrieb:

    Was meinst du mit "spürbar"? Das der Verbrauch extrem steigt? Aber würde er das nicht auch bei einer größeren Funktion (bzw sogar noch mehr? )

    Nehmen wir mal an du rufst eine Funktion mit einem Parameter (unsigned int) auf, dann werden beim Funktionsaufruf 8 byte (4 für die Rücksprungadresse, 4 für den integer) auf den Stack gepusht. Anschließend machst du in der Funktion ein (sinnloses) erhöhen der übergebenen integer variable. Das macht einen 4byte move. Anschließend wird zurückgesprungen, das macht wieder 8 byte (4 für die Integervariable welche einfach gepopt wird und 4 für die Rücksprungadresse).
    Insgesamt werden 8 + 4 + 8 = 20 byte in den speicher geschrieben bzw. gelsen. Nun ist der Overhead aber (8 + 😎 / 20, d.h. 4/5 = 80%. Je mehr du jetzt in der Funktion machst desto kleiner wird der Anteil des Overheads an der Speichernutzung => der Overhead wird immer weniger spürbar mit der Zunahme der größe der Funktion



  • Super, klasse erklärt. Danke 👍



  • EDIT: Hatte einen Denkfehler drinn, hab es überarbeitet.

    Ich hab mal was mit Templates zusammen gebastelt, vielleicht hilft das ja:

    #include <iostream>
    
    template <int Int>
    struct Int2Type {
        enum {
            value = Int
        };
    };
    
    template <int Fors>
    class MegaFor : public MegaFor<Fors - 1> {
        public:
            using MegaFor<Fors - 1>::Value;
            bool Done() {
                if(MegaFor<Fors - 1>::Done()) {
                    bool result = (i == Count_);
                    if(result) {
                        i = 0;
                    } else {
                        ++i;
                    }
                    return result;
                }
                return false;
            }
            MegaFor(int Count) : MegaFor<Fors - 1>(Count), Count_(Count - 1), i(0) {}
            template <int n>
            int v() {
                return Value(Int2Type<Fors - n>());
            }
        protected:
            int Value(Int2Type<Fors>) {
                return i;
            }
        private:
            int Count_;
            int i;
    };
    
    template <>
    class MegaFor<0> {
        public:
            MegaFor(int) { }
            void Value();
            bool Done() {
                return true;
            }
    };
    
    int main() {
        MegaFor<3> f(4); // 3 = Anzahl der For-Schleifen, 4 = Anzahl Durchläufe pro Schleife
        do {
            std::cout << f.v<0>() << " " // Die Zahl sagt, welches i wir haben wollen,
                      << f.v<1>() << " " //  also von welcher Schleife
                      << f.v<2>() << " " << std::endl;
        } while(!f.Done());
    
        /* ist das gleiche wie:
        for(int i = 0; i < 4; ++i) {
            for(int j = 0; j < 4; ++j) {
                for(int k = 0; k < 4; ++k) {
                    std::cout << i << " " << j << " " << k << std::endl;
                }
            }
        }*/
    }
    

    Ausgabe:

    0 0 0
    0 0 1
    0 0 2
    0 0 3
    0 1 0
    0 1 1
    0 1 2
    0 1 3
    0 2 0
    0 2 1
    0 2 2
    0 2 3
    0 3 0
    0 3 1
    0 3 2
    0 3 3
    1 0 0
    1 0 1
    1 0 2
    1 0 3
    1 1 0
    1 1 1
    1 1 2
    1 1 3
    1 2 0
    1 2 1
    1 2 2
    1 2 3
    1 3 0
    1 3 1
    1 3 2
    1 3 3
    2 0 0
    2 0 1
    2 0 2
    2 0 3
    2 1 0
    2 1 1
    2 1 2
    2 1 3
    2 2 0
    2 2 1
    2 2 2
    2 2 3
    2 3 0
    2 3 1
    2 3 2
    2 3 3
    3 0 0
    3 0 1
    3 0 2
    3 0 3
    3 1 0
    3 1 1
    3 1 2
    3 1 3
    3 2 0
    3 2 1
    3 2 2
    3 2 3
    3 3 0
    3 3 1
    3 3 2
    3 3 3
    

    100 und mehr Schleifen sind dann damit auch möglich. Hier ein Beispiel für 4 Schleifen, die bis 6 zählen:

    int main() {
        MegaFor<4> f(6);
        do {
            std::cout << f.v<0>() << " " // Die Zahl sagt, welches i wir haben wollen,
                      << f.v<1>() << " " //  also von welcher Schleife 
                      << f.v<2>() << " "
                      << f.v<3>() << std::endl;
        } while(!f.Done());
    }
    

    Da sollte das gleiche rauskommen, als hätte man vier for-schleifen benutzt, mit i < 6.

    mfg.



  • Vielen Dank für die zahlreichen Beteiligungen und das Template.



  • Mit dem C++Builder 5 Professional bekomme ich in Zeile:

    using MegaFor<Fors - 1>::Value;
    

    folgenden Compilerfehler:

    F1004 Interner Compiler-Fehler at 0x12542d7 with base 0x1200000

    Kann mir jemand weiterhelfen?



  • Versuch dann mal folgendes:

    template <int Fors> 
    class MegaFor : public MegaFor<Fors - 1> { 
        public: 
            //using MegaFor<Fors - 1>::Value;  // auskommentieren
            bool Done() { 
                if(MegaFor<Fors - 1>::Done()) { 
                    bool result = (i == Count_); 
                    if(result) { 
                        i = 0; 
                    } else { 
                        ++i; 
                    } 
                    return result; 
                } 
                return false; 
            } 
            MegaFor(int Count) : MegaFor<Fors - 1>(Count), Count_(Count - 1), i(0) {} 
            template <int n> 
            int v() { 
                return MegaFor<Fors - n>::Value(Int2Type<Fors - n>()); // hier Typ einfügen
            } 
        public: // statt protected
            int Value(Int2Type<Fors>) { 
                return i; 
            } 
        private: 
            int Count_; 
            int i; 
    };
    

    Evtl. reicht es auch aus, einfach nur die using-Anweisung auszukommentieren.


Anmelden zum Antworten