Frage

Mit anderen Worten, kann ich so etwas wie

for() {
    for {
       for {
       }
    }
}

Außer n mal? Mit anderen Worten, wenn die Methode, die die Schleifen erstellt, aufgerufen wird, erhält sie einen Parameter n, und die Methode würde dann N dieser Schleifen in einem anderen verschachtelt?

Natürlich ist die Idee, dass es eine "einfache" oder "übliche" Art zu tun sein sollte. Ich habe bereits eine Idee für eine sehr komplizierte.

War es hilfreich?

Lösung

Es hört sich so an, als ob Sie vielleicht schauen möchten Rekursion.

Andere Tipps

Jjnguy hat recht; Durch Rekursion können Sie dynamisch variable Tiefe nisten. Sie erhalten jedoch keinen Zugriff auf Daten aus den äußeren Schichten ohne etwas mehr Arbeit. Der Fall "Inline-nestiert":

for (int i = lo; i < hi; ++i) {
    for (int j = lo; j < hi; ++j) {
        for (int k = lo; k < hi; ++k) {
            // do something **using i, j, and k**
        }
    }
}

Hält die Variablen i, j, und k im Bereich für den innersten Körper zu verwenden.

Hier ist ein kurzer Hack, um das zu tun:

public class NestedFor {

    public static interface IAction {
        public void act(int[] indices);
    }

    private final int lo;
    private final int hi;
    private final IAction action;

    public NestedFor(int lo, int hi, IAction action) {
        this.lo = lo;
        this.hi = hi;
        this.action = action;
    }

    public void nFor (int depth) {
        n_for (0, new int[0], depth);
    }

    private void n_for (int level, int[] indices, int maxLevel) {
        if (level == maxLevel) {
            action.act(indices);
        } else {
            int newLevel = level + 1;
            int[] newIndices = new int[newLevel];
            System.arraycopy(indices, 0, newIndices, 0, level);
            newIndices[level] = lo;
            while (newIndices[level] < hi) {
                n_for(newLevel, newIndices, maxLevel);
                ++newIndices[level];
            }
        }
    }
}

Das IAction Die Schnittstelle sieht die Rolle einer kontrollierten Aktion vor, die eine Reihe von Indizes als Argument für ihre act Methode.

In diesem Beispiel jede Instanz von NestedFor wird vom Konstruktor mit den Iterationsgrenzen und der Aktion konfiguriert, die von der innersten Ebene ausgeführt werden soll. Der Parameter der nFor Methode gibt an, wie tief das Nest ist.

Hier ist eine Beispielverwendung:

public static void main(String[] args) {
    for (int i = 0; i < 4; ++i) {
        final int depth = i;
        System.out.println("Depth " + depth);
        IAction testAction = new IAction() {
            public void act(int[] indices) {
                System.out.print("Hello from level " + depth + ":");
                for (int i : indices) { System.out.print(" " + i); }
                System.out.println();
            }
        };
        NestedFor nf = new NestedFor(0, 3, testAction);
        nf.nFor(depth);
    }
}

und die (teilweise) Ausgabe aus seiner Ausführung:

Depth 0
Hello from level 0:
Depth 1
Hello from level 1: 0
Hello from level 1: 1
Hello from level 1: 2
Depth 2
Hello from level 2: 0 0
Hello from level 2: 0 1
Hello from level 2: 0 2
Hello from level 2: 1 0
Hello from level 2: 1 1
Hello from level 2: 1 2
Hello from level 2: 2 0
Hello from level 2: 2 1
Hello from level 2: 2 2
Depth 3
Hello from level 3: 0 0 0
Hello from level 3: 0 0 1
Hello from level 3: 0 0 2
Hello from level 3: 0 1 0
...
Hello from level 3: 2 1 2
Hello from level 3: 2 2 0
Hello from level 3: 2 2 1
Hello from level 3: 2 2 2

Vielleicht möchten Sie erklären, was Sie wirklich tun möchten.

Wenn die Außen for Loops tun nur, als eine Zählung zu kontrollieren, dann Ihre verschachtelte for Loops sind einfach eine kompliziertere Art der Iteration durch eine Zählung, die von einer einzigen behandelt werden könnte for Schleife.

Zum Beispiel:

for (x = 0; x < 10; ++x) {
  for (y = 0; y < 5; ++y) {
    for (z = 0; z < 20; ++z) {
      DoSomething();
    }
  }
}

Ist äquivalent zu:

for (x = 0; x < 10*5*20; ++x) {
  DoSomething();
}

Ich habe neulich darüber nachgedacht.

Ein Beispiel, das wahrscheinlich nicht perfekt ist, aber ziemlich nahe an dem, was meiner Meinung nach gefragt wird, würde einen Verzeichnisbaum ausdrucken

public void printTree(directory) {
   for(files in directory) {
      print(file);
      if(file is directory) {
          printTree(file);
      }
   }
}

Auf diese Weise haben Sie einen Stapel von ineinander verschachtelten Schleifen, ohne dass genau er herausgefunden hat, wie sie zusammengehen sollten.

2015 Bearbeiten: Entlang der gleichen eitel wie die vorherige Beschwörung habe ich das folgende Paket gemacht, um dies zu behandeln. https://github.com/beundead/nfor

Die Verwendung wäre wie folgt

public static void main(String... args) {
    NFor<Integer> nfor = NFor.of(Integer.class)
            .from(0, 0, 0)
            .by(1, 1, 1)
            .to(2, 2, 3);

    for (Integer[] indices : nfor) {
        System.out.println(java.util.Arrays.toString(indices));
    }
}

ergebend

[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 1, 0]
[0, 1, 1]
[0, 1, 2]
[1, 0, 0]
[1, 0, 1]
[1, 0, 2]
[1, 1, 0]
[1, 1, 1]
[1, 1, 2]

Es unterstützt auch andere Bedingungen als lessThan. Die Verwendung dort ist (mit import static NFor.*;):

NFor<Integer> nfor = NFor.of(Integer.class)
        .from(-1, 3, 2)
        .by(1, -2, -1)
        .to(lessThanOrEqualTo(1), greaterThanOrEqualTo(-1), notEqualTo(0));

Ergebend:

[-1, 3, 2]
[-1, 3, 1]
[-1, 1, 2]
[-1, 1, 1]
[-1, -1, 2]
[-1, -1, 1]
[0, 3, 2]
[0, 3, 1]
[0, 1, 2]
[0, 1, 1]
[0, -1, 2]
[0, -1, 1]
[1, 3, 2]
[1, 3, 1]
[1, 1, 2]
[1, 1, 1]
[1, -1, 2]
[1, -1, 1]

Offensichtlich werden Schleifen unterschiedlicher Längen und unterschiedlichen Klassen (alle boxen, numerischen Primitiven) unterstützt. Der Standard (falls nicht angegeben) stammt von (0, ...). By (1, ...); Aber a bis (...) muss angegeben werden.

Das NForTest Die Datei sollte verschiedene Möglichkeiten zeigen, sie zu verwenden.

Die grundlegende Prämisse davon ist, einfach die "Indizes" in jeder Runde voranzutreiben, anstatt Rekursion zu verwenden.

Problem erfordert mehr Spezifikation. Vielleicht hilft Ihnen eine Rekursion, aber denken Sie daran, dass Rekursion fast immer eine Alternative zur Iteration ist und umgekehrt. Es kann sein, dass eine 2-stufige verschachtelte Schleife für Ihre Bedürfnisse ausreichen kann. Lassen Sie uns einfach wissen, welches Problem Sie lösen möchten.

Die wesentliche Idee hinter Nisting Loops ist Multiplikation.

Erweiterung der Antwort von Michael Burr, wenn die Außenseite for Loops tun nur, als eine Zählung zu kontrollieren, dann Ihre verschachtelte for Schleifen vorbei n Zählungen sind einfach eine kompliziertere Art, über das Produkt der Zählungen mit einem einzigen zu iterieren for Schleife.

Lassen Sie uns diese Idee nun auf Listen erweitern. Wenn Sie über drei Listen in verschachtelten Schleifen iterieren, ist dies einfach eine kompliziertere Möglichkeit, das Produkt der Listen mit einer einzigen Schleife zu iterieren. Aber wie drücken Sie das Produkt von drei Listen aus?

Erstens brauchen wir eine Möglichkeit, das Produkt von Typen auszudrücken. Das Produkt von zwei Arten X und Y kann als generischer Typ ausgedrückt werden wie P2<X, Y>. Dies ist nur ein Wert, der aus zwei Werten besteht X, der andere vom Typ Y. Es sieht aus wie das:

public abstract class P2<A, B> {
  public abstract A _p1();
  public abstract B _p2();
}

Für ein Produkt von drei Typen haben wir nur P3<A, B, C>, mit der offensichtlichen dritten Methode. Ein Produkt von drei Listen wird also erreicht, indem der Listenfunktionstyp über den Produkttyp verteilt wird. Also das Produkt von List<X>, List<Y>, und List<Z> ist einfach List<P3<X, Y, Z>>. Sie können dann diese Liste mit einer einzigen Schleife iterieren.

Das Funktionaler Java Bibliothek hat a List Typ, der die Multiplikation von Listen mithilfe von erstklassigen Funktionen und Produkttypen miteinander unterstützt (P2, P3 usw., die auch in der Bibliothek enthalten sind).

Zum Beispiel:

for (String x : xs) {
   for (String y : ys) {
     for (String z : zs) {
       doSomething(x, y, z);
     }
   }
}

Ist äquivalent zu:

for (P3<String, String, String> p : xs.map(P.p3()).apply(ys).apply(zs)) {
   doSomething(p._1(), p._2(), p._3());
}

Wenn Sie weiter mit funktionaler Java gehen, können Sie machen doSomething Erstklasse wie folgt. Sagen wir doSomething Gibt eine Zeichenfolge zurück:

public static final F<P3<String, String, String>, String> doSomething =
  new F<P3<String, String, String>, String>() {
    public String f(final P3<String, String, String> p) {
      return doSomething(p._1(), p._2(), p._3());
    }
  };

Dann können Sie die For-Loop insgesamt beseitigen und die Ergebnisse aller Anwendungen von sammeln doSomething:

List<String> s = xs.map(P.p3()).apply(ys).apply(zs).map(doSomething);

Wenn Sie eine allgemeine Struktur für verschachtelte Schleife haben wie:

for(i0=0;i0<10;i0++)
    for(i1=0;i1<10;i1++)
        for(i2=0;i2<10;i2++)
            ....
                for(id=0;id<10;id++)
                    printf("%d%d%d...%d\n",i0,i1,i2,...id);

wo i0,i1,i2,...,id sind Schleifenvariablen und d ist die Tiefe der verschachtelten Schleife.

Äquivalente Rekursionslösung:

void nestedToRecursion(counters,level){
    if(level == d)
        computeOperation(counters,level);
    else
    {
        for (counters[level]=0;counters[level]<10;counters[level]++)
            nestedToRecursion(counters,level+1);
    }
}
void computeOperation(counters,level){
    for (i=0;i<level;i++)
        printf("%d",counters[i]);
    printf("\n");
}

Zähler sind eine Reihe von Größe d, die entsprechenden Variablen darstellen i0,i1,i2,...id beziehungsweise int counters[d].

nestedToRecursion(counters,0);

In ähnlicher Weise können wir andere Variablen wie die Initialisierung von Rekursion oder das Ende mit Arrays für sie konvertieren, dh wir könnten haben initial[d], ending[d].

Der schönste allgemeine Ansatz, den ich in Java 7 finden könnte, ist

// i[0] = 0..1  i[1]=0..3, i[2]=0..4
MultiForLoop.loop( new int[]{2,4,5}, new MultiForLoop.Callback() { 
    void act(int[] i) { 
        System.err.printf("%d %d %d\n", i[0], i[1], i[2] );
    }
}

Oder in Java 8:

// i[0] = 0..1  i[1]=0..3, i[2]=0..4
MultiForLoop.loop( new int[]{2,4,5}, 
   i -> { System.err.printf("%d %d %d\n", i[0], i[1], i[2]; } 
);

Eine Implementierung, die dies unterstützt, ist:

/**
 * Uses recursion to perform for-like loop.
 *  
 * Usage is 
 *  
 *    MultiForLoop.loop( new int[]{2,4,5}, new MultiForLoop.Callback() { 
 *        void act(int[] indices) { 
 *            System.err.printf("%d %d %d\n", indices[0], indices[1], indices[2] );
 *        }
 *    }
 *  
 * It only does 0 - (n-1) in each direction, no step or start 
 * options, though they could be added relatively trivially.
 */
public class MultiForLoop {

    public static interface Callback {
        void act(int[] indices);
    }

    static void loop(int[] ns, Callback cb) {
        int[] cur = new int[ns.length];
        loop(ns, cb, 0, cur);
    }

    private static void loop(int[] ns, Callback cb, int depth, int[] cur) {
        if(depth==ns.length) {
            cb.act(cur);
            return;
        }

        for(int j = 0; j<ns[depth] ; ++j ) {
            cur[depth]=j;
            loop(ns,cb, depth+1, cur);
        }
    }
}
String fors(int n){
StringBuilder bldr = new StringBuilder();
for(int i = 0; i < n; i++){
    for(int j = 0; j < i; j++){
        bldr.append('\t');
    }
    bldr.append("for() {\n");
}
for(int i = n-1; i >= 0; i--){
    for(int j = 0; j < i; j++){
        bldr.append('\t');
    }
    bldr.append("}\n");
}
return bldr.toString();
}

Schafft ein nettes verschachteltes, schlechtes Skelett ;-) Nicht ganz ernst und ich bin mir bewusst, dass eine rekursive Lösung eleganter gewesen wäre.

public void recursiveFor(Deque<Integer> indices, int[] ranges, int n) {

    if (n != 0) {

       for (int i = 0; i < ranges[n-1]; i++) {

          indices.push(i);
          recursiveFor(indices, ranges, n-1);
          indices.pop();
       }
    }

    else {

       // inner most loop body, access to the index values thru indices
       System.out.println(indices);
    }
}

Beispielanruf:

int[] ranges = {2, 2, 2};

recursiveFor(new ArrayDeque<Integer>(), ranges, ranges.length);

Ich beantworte eine Frage zum ersten Mal, aber ich hatte das Gefühl, dass ich diese Informationen von `teilen musste

for (x = 0; x < base; ++x) {
  for (y = 0; y < loop; ++y) {
      DoSomething();
  }
}

gleichwertig zu sein

for (x = 0; x < base*loop; ++x){
    DoSomething();
}

Wenn Sie also eine N -Anzahl von Nestern wollten, kann sie mit der Teilung zwischen der Trennung geschrieben werden base und loop Es könnte also etwas so Einfaches aussehen:

char[] numbs = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
     public void printer(int base, int loop){
       for (int i = 0; i < pow(base, loop); i++){
         int remain = i;
         for (int j = loop-1; j >= 0; j--){
           int digit = remain/int(pow(base, j));
           print(numbs[digit]);
           remain -= digit*pow(base, j);
         }
         println();
       }
     }

Also, wenn Sie tippen würden printer(10, 2); es würde ausdrucken:

00
01
02
03
04
...
97
98
99

Dies funktionierte für mich wirklich nett - ich musste aus einigen Alternativen auswählen, die in myalternativen Pattern gespeichert wurden, und die Grundidee ist, dass ich versucht habe, die nächste Auswahl zu konstruieren, und wenn es einen "Überlauf" in einer Dimension / Komponente gab, nur Initialisieren Sie diese Dimension neu und fügen Sie einen zum nächsten hinzu.

public boolean isValidAlternativeSelection (int[] alternativesSelected) {
    boolean allOK = true;
    int nPaths= myAlternativePaths.size();
    for (int i=0; i<nPaths; i++) {
        allOK=allOK & (alternativesSelected[i]<myAlternativePaths.get(i).myAlternativeRoutes.size());
    }
    return allOK;
}


public boolean getNextValidAlternativeSelection (int[] alternativesSelected) {
    boolean allOK = true;
    int nPaths= myAlternativePaths.size();
    alternativesSelected[0]=alternativesSelected[0]+1;
    for (int i=0; i<nPaths; i++) {
        if (alternativesSelected[i]>=myAlternativePaths.get(i).myAlternativeRoutes.size()) {
            alternativesSelected[i]=0;
            if(i<nPaths-1) {
                alternativesSelected[i+1]=alternativesSelected[i+1]+1;
            } else {
                allOK = false;
            }
        }
 //       allOK=allOK & (alternativesSelected[i]<myAlternativePaths.get(i).myAlternativeRoutes.size());
    }
    return allOK;
}

Im Interesse der Selbstverständlichkeit stelle ich meinen Code hier ein:

void variDepth(int depth, int n, int i) {
    cout<<"\n d = "<<depth<<" i = "<<i;
    if(!--depth) return;
    for(int i = 0;i<n;++i){
        variDepth(depth,n,i);
    }
}
void testVariDeapth()
{   variDeapth(3, 2,0);
}

Ausgabe

 d = 3 i = 0
 d = 2 i = 0
 d = 1 i = 0
 d = 1 i = 1
 d = 2 i = 1
 d = 1 i = 0
 d = 1 i = 1
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top