usando repetitivos que problemas for ejercicios ejercicio ejemplos con ciclos bucles bucle anidado java recursion loops for-loop

repetitivos - problemas con ciclos java



¿Hay alguna manera de hacer bucles anidados de n niveles en Java? (13)

En otras palabras, ¿puedo hacer algo como

for() { for { for { } } }

Excepto N veces? En otras palabras, cuando se llama al método que crea los bucles, se le da algún parámetro N, y el método entonces crearía N de estos bucles anidados uno en otro?

Por supuesto, la idea es que debe haber una forma "fácil" o "habitual" de hacerlo. Ya tengo una idea para una muy complicada.


El mejor enfoque general que pude encontrar en Java 7 es

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

Una implementación que admite esto es:

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


El problema necesita más especificaciones. Tal vez la recursión te ayude, pero ten en cuenta que la recursión es casi siempre una alternativa a la iteración, y viceversa. Es posible que un bucle anidado de dos niveles sea suficiente para sus necesidades. Simplemente díganos qué problema está tratando de resolver.


En aras de la concisión, estoy poniendo mi código aquí:

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

Salida

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


Es posible que desee explicar lo que realmente quiere hacer.

Si los bucles for externos no hacen más que controlar un conteo, entonces los bucles for anidados son simplemente una forma más complicada de iterar mediante un conteo que podría manejarse con un solo bucle for .

Por ejemplo:

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

Es equivalente a:

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


Estaba pensando en esto el otro día.

Un ejemplo que probablemente no sea perfecto pero bastante cercano a lo que creo que se está haciendo sería imprimir un árbol de directorios

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

de esta forma terminas con una pila de bucles for nidados uno dentro del otro, sin la molestia de averiguar exactamente cómo deben ir juntos.


La idea esencial detrás de los bucles de anidación es la multiplicación .

Ampliando la respuesta de Michael Burr, si los bucles for externos no hacen más que controlar un conteo, entonces sus bucles anidados for n count son simplemente una forma más complicada de iterar sobre el producto de los conteos con un solo bucle for .

Ahora, extiendamos esta idea a las Listas. Si está iterando sobre tres listas en bucles anidados, esta es simplemente una forma más complicada de iterar sobre el producto de las listas con un solo bucle. ¿Pero cómo expresas el producto de tres listas?

Primero, necesitamos una forma de expresar el producto de los tipos. El producto de dos tipos X e Y se puede expresar como un tipo genérico como P2<X, Y> . Este es solo un valor que consta de dos valores, uno de tipo X y el otro de tipo Y Se parece a esto:

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

Para un producto de tres tipos, solo tenemos P3<A, B, C> , con el tercer método obvio. Entonces, se obtiene un producto de tres listas distribuyendo el funtor de lista sobre el tipo de producto. Entonces el producto de List<X> , List<Y> , y List<Z> es simplemente List<P3<X, Y, Z>> . A continuación, puede iterar sobre esta lista con un solo bucle.

La biblioteca Java funcional tiene un tipo de List que admite la multiplicación de listas utilizando funciones de primera clase y tipos de productos (P2, P3, etc. que también se incluyen en la biblioteca).

Por ejemplo:

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

Es equivalente a:

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

Yendo más allá con Functional Java, puede hacer doSomething primera clase, de la siguiente manera. Digamos que algo devuelve una cadena:

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

Luego puede eliminar por completo el for-loop y recopilar los resultados de todas las aplicaciones de doSomething :

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



Si tiene una estructura general de bucle anidado como:

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

donde i0,i1,i2,...,id son variables de bucle d es la profundidad del bucle anidado.

Solución de Recursión 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<level;i++) printf("%d",counters[i]); printf("/n"); }

counters es una matriz de tamaño d , que representa las variables correspondientes i0,i1,i2,...id respectivamente int counters[d] .

nestedToRecursion(counters,0);

Del mismo modo, podemos convertir otras variables, como la inicialización de la recursión o el final mediante el uso de matrices para ellos, es decir, podríamos tener la initial[d], ending[d] .


jjnguy tiene razón; recursividad le permite crear dinámicamente un anidamiento de profundidad variable. Sin embargo, no tienes acceso a los datos de las capas externas sin un poco más de trabajo. El caso "anidado en línea":

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 las variables i , j y k en el alcance para que lo use el cuerpo más interno.

Aquí hay un truco rápido para hacer eso:

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

La interfaz IAction estipula el rol de una acción controlada que toma una matriz de índices como el argumento de su método act .

En este ejemplo, el constructor configura cada instancia de NestedFor con los límites de iteración y la acción que realizará el nivel más interno. El parámetro del método nFor especifica qué tan profundamente anidar.

Aquí hay un uso de muestra:

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

y el resultado (parcial) de su ejecución:

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


la primera vez que respondí una pregunta, pero sentí que necesitaba compartir esta información de `

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

siendo equivalente a

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

así que si quieres una n cantidad de nidos, se puede escribir usando la división entre la base y el loop para que se vea algo tan simple como esto:

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

así que si escribes la printer(10, 2); se imprimiría:

00 01 02 03 04 ... 97 98 99


Edición de 2015: Por la misma vana que el conjuro anterior, hice el siguiente paquete para manejar esto; https://github.com/BeUndead/NFor

El uso sería el siguiente

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

Resultando en

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

También admite condiciones distintas de lessThan . El uso allí es (con 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));

Resultando en:

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

Obviamente, se admiten bucles de diferentes longitudes y diferentes clases (todos en caja, primitivas numéricas). El valor predeterminado (si no se especifica) es de (0, ...). Por (1, ...); pero debe especificarse a (...).

El archivo NForTest debe mostrar varias formas diferentes de usarlo.

La premisa básica de esto es simplemente avanzar los ''índices'' cada turno en lugar de usar la recursión.


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 bonito esqueleto de bucle forzado ;-) No es del todo serio y soy consciente de que una solución recursiva hubiera sido más elegante.


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

Ejemplo de llamada:

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