Existe-t-il un moyen de faire des boucles imbriquées de niveau n en Java?


En d'autres termes, puis-je faire quelque chose comme

for() {
    for {
       for {
       }
    }
}

Sauf N fois? En d'autres termes, lorsque la méthode créant les boucles est appelée, on lui donne un paramètre N, et la méthode créerait alors N de ces boucles imbriquées les unes dans les autres?

Bien sûr, l'idée est qu'il devrait être "facile" ou "l'habituel" façon de faire. J'ai déjà une idée pour un très compliquées.

Author: jjnguy, 2009-01-09

13 answers

Il semble que vous souhaitiez peut-être examiner la récursivité .

 20
Author: jjnguy, 2009-04-21 02:25:01

Jjnguy a raison; la récursivité vous permet de créer dynamiquement une imbrication à profondeur variable. Cependant, vous n'avez pas accès aux données des couches externes sans un peu plus de travail. Le cas "imbriqué en ligne":

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

Conserve les variables i, j, et k dans la portée pour le corps le plus intérieur à utiliser.

Voici un hack rapide pour le faire:

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'interface IAction stipule le rôle d'une action contrôlée qui prend un tableau d'indices comme argument à son act méthode.

Dans cet exemple, chaque instance de NestedFor est configurée par le constructeur avec les limites d'itération et l'action à effectuer par le niveau le plus interne. Le paramètre de la méthode nFor spécifie la profondeur à imbriquer.

Voici un exemple d'utilisation:

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

Et la sortie (partielle) de son exécution:

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
 33
Author: joel.neely, 2009-01-09 04:43:53

Vous pourriez expliquer ce que vous voulez vraiment faire.

Si les boucles for externes ne font que contrôler un nombre, alors vos boucles for imbriquées sont simplement un moyen plus compliqué d'itérer par un nombre qui pourrait être géré par une seule boucle for.

Par exemple:

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

Est équivalent à:

for (x = 0; x < 10*5*20; ++x) {
  DoSomething();
}
 6
Author: Michael Burr, 2009-01-09 02:49:52

J'étais en train de penser à propos de l'autre jour.

Un exemple qui n'est probablement pas parfait mais assez proche de ce que je pense être demandé serait d'imprimer une arborescence de répertoires

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

De cette façon, vous vous retrouvez avec une pile de boucles for imbriquées les unes dans les autres, sans avoir à déterminer exactement comment elles devraient aller ensemble.

 5
Author: Wayne, 2009-01-09 03:43:03

2015 Edit: Le long du même vain que l'incantation précédente, j'ai fait le paquet suivant pour gérer cela; https://github.com/BeUndead/NFor

L'utilisation serait la suivante

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

Résultant 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]

Il prend également en charge des conditions autres que lessThan. L'utilisation qu'il y a (avec 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));

, Résultant 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]

Évidemment, les boucles de différentes longueurs et de différentes classes (toutes les primitives numériques en boîte) sont soutenu. La valeur par défaut (si non spécifiée) est from(0, ...).by(1, ...); mais un à (...) doit être spécifié.

Le fichier NForTest doit démontrer plusieurs façons différentes de l'utiliser.

La prémisse de base de cela étant de simplement avancer les "indices" à chaque tour plutôt que d'utiliser la récursivité.

 5
Author: user2478398, 2015-10-27 01:13:27

Le problème nécessite plus de spécifications. Peut-être que la récursivité vous aidera, mais gardez à l'esprit que la récursivité est presque toujours une alternative à l'itération, et vice versa. Il se peut qu'une boucle imbriquée à 2 niveaux puisse suffire à vos besoins. Faites-nous savoir quel problème vous essayez de résoudre.

 3
Author: John Zwinck, 2009-01-09 02:44:08

L'idée essentielle derrière les boucles d'imbrication est multiplication.

En développant la réponse de Michael Burr, si les boucles for externes ne font rien d'autre que contrôler un nombre, alors vos boucles for imbriquées sur n sont simplement un moyen plus compliqué d'itérer sur le produit des comptes avec une seule boucle for.

Maintenant, étendons cette idée aux listes. Si vous parcourez trois listes dans des boucles imbriquées, c'est simplement un moyen plus compliqué de itérer sur le produit des listes avec une seule boucle. Mais comment exprimer le produit de trois listes?

Premièrement, nous avons besoin d'une façon d'exprimer le produit des types. Le produit de deux types de X et Y, peut être exprimé comme un type générique comme P2<X, Y>. C'est juste une valeur qui se compose de deux valeurs, l'une de type X, l'autre de type Y. Cela ressemble à ceci:

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

Pour un produit de trois types, nous avons juste P3<A, B, C>, avec l'évidente troisième méthode. Produit de trois listes, puis, est obtenu en distribuant le foncteur de liste sur le type de produit. Donc, le produit de List<X>, List<Y>, et List<Z> est simplement List<P3<X, Y, Z>>. Vous pouvez ensuite parcourir cette liste avec une seule boucle.

La bibliothèque fonctionnelle Java a un type List qui prend en charge la multiplication des listes ensemble en utilisant des fonctions de première classe et des types de produits (P2, P3, etc. qui sont également inclus dans la bibliothèque).

Par exemple:

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

Est équivalent pour:

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

Aller plus loin avec Java fonctionnel, vous pouvez faire doSomething de première classe, comme suit. Disons que doSomething renvoie une chaîne:

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

Ensuite, vous pouvez éliminer complètement la boucle for et collecter les résultats de toutes les applications de doSomething:

List<String> s = xs.map(P.p3()).apply(ys).apply(zs).map(doSomething);
 3
Author: Apocalisp, 2009-01-09 05:23:58

Si vous avez une structure de boucle imbriquée générale comme:

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

i0,i1,i2,...,id sont des variables de boucle et d est la profondeur de la boucle imbriquée.

Solution de récursivité équivalente:

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

Compteurs est un tableau de taille d, représentant les variables correspondantes i0,i1,i2,...id, respectivement int counters[d].

nestedToRecursion(counters,0);

De même, nous pouvons convertir d'autres variables comme l'initialisation de la récursivité ou la fin en utilisant des tableaux pour elles, c'est-à-dire que nous pourrions avoir initial[d], ending[d].

 2
Author: Deepak Sharma, 2015-09-19 13:25:54

L'approche générale la plus soignée que j'ai pu trouver dans Java 7 est

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

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

Une implémentation qui prend en charge ceci est:

/**
 * 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);
        }
    }
}
 1
Author: Michael Anderson, 2015-07-06 07:10:05
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();
}

Crée un joli squelette for-loop imbriqué ;-) Pas complètement sérieux et je suis conscient qu'une solution récursive aurait été plus élégante.

 0
Author: Michael Kutschke, 2011-08-22 10:22:19
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);
    }
}

Exemple d'appel:

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

recursiveFor(new ArrayDeque<Integer>(), ranges, ranges.length);
 0
Author: gewizz, 2012-05-20 04:02:22

C'est la première fois que je répondais à une question mais j'avais envie de partager cette information de `

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

Étant équivalent à

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

Donc, si vous vouliez un n nombre de nids, il peut être écrit en utilisant la division entre base et {[5] } afin que cela puisse paraître aussi simple que ceci:

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

Donc, si vous deviez taper printer(10, 2);, il imprimerait:

00
01
02
03
04
...
97
98
99
 0
Author: user3239534, 2017-02-11 18:58:23

Dans l'intérêt de la concision, je mets mon code ici:

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

Sortie

 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
 -1
Author: Ajeet Ganga, 2013-02-18 08:18:36