C’è un modo per fare loop nidificati a livello n in Java?

In altre parole, posso fare qualcosa di simile

for() { for { for { } } } 

Tranne N volte? In altre parole, quando viene chiamato il metodo che crea i loop, gli viene assegnato un parametro N, e il metodo creerebbe quindi N di questi loop nidificati uno nell’altro?

Certo, l’idea è che ci dovrebbe essere un modo “facile” o “normale” per farlo. Ho già un’idea per una molto complicata.

Sembra che tu possa voler guardare in ricorsione.

jjnguy ha ragione; la ricorsione ti consente di creare dynamicmente la nidificazione a profondità variabile. Tuttavia, non hai accesso ai dati dagli strati esterni senza un po ‘più di lavoro. Il caso “in-line-annidato”:

 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** } } } 

mantiene le variabili i , j e k in scope per il corpo più interno da usare.

Ecco un trucco veloce per farlo:

 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]; } } } } 

L'interfaccia IAction stabilisce il ruolo di un'azione controllata che prende una serie di indici come argomento del suo metodo act .

In questo esempio, ogni istanza di NestedFor è configurata dal costruttore con i limiti di iterazione e l'azione da eseguire dal livello più interno. Il parametro del metodo nFor specifica quanto profondamente annidare.

Ecco un esempio di utilizzo:

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

e il (parziale) risultato dalla sua esecuzione:

 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 

Potresti voler spiegare cosa vuoi veramente fare.

Se i cicli esterni non fanno altro che controllare un conteggio, i cicli annidati for loop sono semplicemente un modo più complicato di iterare con un conteggio che può essere gestito da un ciclo for .

Per esempio:

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

È equivalente a:

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

In realtà stavo pensando a questo l’altro giorno.

Un esempio che probabilmente non è perfetto ma piuttosto vicino a quello che penso venga chiesto sarebbe la stampa di un albero di directory

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

in questo modo si finisce con una pila di loop for nidificati l’uno dentro l’altro, senza il fastidio di capire esattamente come dovrebbero andare insieme.

2015 Modifica: insieme allo stesso incantesimo precedente, ho creato il seguente pacchetto per gestirlo; https://github.com/BeUndead/NFor

L’utilizzo sarebbe come segue

 public static void main(String... args) { NFor 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)); } } 

con il risultato di

 [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] 

Supporta anche condizioni diverse da lessThan . L’utilizzo è in corso (con import static NFor.*; ):

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

Con il risultato di:

 [-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] 

Ovviamente, sono supportati cicli di lunghezze diverse e classi diverse (tutte primitive numeriche in scatola). L’impostazione predefinita (se non specificata) proviene da (0, …). Da (1, …); ma a (…) deve essere specificato.

Il file NForTest dovrebbe dimostrare diversi modi per usarlo.

La premessa di base di questo essere è semplicemente quella di far avanzare gli “indici” ogni turno piuttosto che ricorrere alla ricorsione.

Il problema richiede più specifiche. Forse la ricorsione ti aiuterà, ma tieni presente che la ricorsione è quasi sempre un’alternativa all’iterazione e viceversa. Può essere che un ciclo annidato su 2 livelli possa essere sufficiente per le tue esigenze. Facci sapere quale problema stai cercando di risolvere.

L’idea essenziale dietro i loop di nidificazione è la moltiplicazione .

Espandendo la risposta di Michael Burr, se i cicli esterni non fanno altro che controllare un conteggio, allora i cicli nidificati for cicli su n conteggi sono semplicemente un modo più complicato per iterare sul prodotto dei conteggi con un ciclo for .

Ora, estendiamo questa idea agli elenchi. Se si sta iterando su tre elenchi nei cicli nidificati, questo è semplicemente un modo più complicato di iterare sul prodotto degli elenchi con un singolo ciclo. Ma come esprimi il prodotto di tre liste?

Innanzitutto, abbiamo bisogno di un modo per esprimere il prodotto dei tipi. Il prodotto di due tipi X e Y può essere express come un tipo generico come P2 . Questo è solo un valore costituito da due valori, uno di tipo X , l’altro di tipo Y Sembra questo:

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

Per un prodotto di tre tipi, abbiamo solo P3 , con l’ovvio terzo metodo. Un prodotto di tre elenchi, quindi, si ottiene distribuendo il functor Elenco sul tipo di prodotto. Quindi il prodotto di List , List e List è semplicemente List> . È quindi ansible scorrere su questa lista con un singolo ciclo.

La libreria Java funzionale ha un tipo di List che supporta la moltiplicazione degli elenchi utilizzando le funzioni di prima class e i tipi di prodotto (P2, P3, ecc. Che sono anche inclusi nella libreria).

Per esempio:

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

È equivalente a:

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

Andando oltre con Functional Java, puoi fare doSomething prima class, come segue. Diciamo che doSomething restituisce una stringa:

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

Quindi puoi eliminare completamente il ciclo for e raccogliere i risultati di tutte le applicazioni di doSomething :

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

Se stai avendo una struttura ad anello annidato generale come:

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

dove i0,i1,i2,...,id sono variabili di ciclo e d è la profondità del ciclo annidato.

Soluzione di ricorsione equivalente:

 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 

i contatori sono una matrice di dimensione d , che rappresenta le corrispondenti variabili i0,i1,i2,...id rispettivamente i int counters[d] .

 nestedToRecursion(counters,0); 

Allo stesso modo possiamo convertire altre variabili come l'inizializzazione della ricorsione o la fine usando array per loro, cioè potremmo avere initial[d], ending[d] .

L’approccio generale più accurato che potrei trovare in Java 7 è

 // 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] ); } } 

O 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]; } ); 

Un’implementazione che supporta questo è:

 /** * 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 
 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(); } 

Crea un bel scheletro annidato per il ciclo 😉 Non completamente serio e sono consapevole che una soluzione ricorsiva sarebbe stata più elegante.

 public void recursiveFor(Deque 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); } } 

Chiamata di esempio:

 int[] ranges = {2, 2, 2}; recursiveFor(new ArrayDeque(), ranges, ranges.length); 

la mia prima volta a rispondere a una domanda, ma mi sentivo come se avessi bisogno di condividere questa informazione di `

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

essere equivalente a

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

quindi se si desidera un n numero di nidi, può essere scritto utilizzando la divisione tra base e loop modo che possa sembrare qualcosa di semplice come questo:

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

quindi se si dovesse digitare printer(10, 2); sarebbe stampato:

 00 01 02 03 04 ... 97 98 99