How to generate inner loops automatically in Java

Viewed 115

I am trying to find a way to generate inner loops on demand (and have the depth as a variable).

In the following example, I am trying to generate a list of references such as jobo.2.2.2.2.2.2 where each .2 is added in the inner loop.

Here is what I have at the moment when I implement the new loop manually for 5 level of depth (i, j, k, l):

 public void buildTaskList(){
    String jobName ="jobo";
    String last="";
    long max=3;
    List<String> tasks = new ArrayList<>();


    for (long i = 1; i <= max; i++) {
        for (long j = 1; j <= max; j++) {
            if (j==max){
                last="*";
                tasks.add(jobName+"."+i+"."+j+last);
            }else {
                last="";
                for (long k = 1; k <= max; k++) {
                    if (k==max){
                        last="*";
                        tasks.add(jobName+"."+i+"."+j+"."+k+last);
                    }else {
                        last="";
                        for (long l = 1; l <= max; l++) {
                            if (l==max){
                                last="*";
                                tasks.add(jobName+"."+i+"."+j+"."+k+"."+l+last);
                            }else{
                                last="";
                                for (long m = 1; m <= max; m++) {
                                    if (m==max){
                                        last="*";
                                        tasks.add(jobName+"."+i+"."+j+"."+k+"."+l+"."+m+last);
                                    }else{
                                        last="";
                                        for (long n = 1; n <= max; n++) {
                                            if (n==max)last="*";else last="";
                                            tasks.add(jobName+"."+i+"."+j+"."+k+"."+l+"."+m+"."+n+last);
                                        }
                                    }

                                }
                            }

                        }
                    }

                }
            }

        }
    }
    tasks.add(jobName+"."+(max+1)+last);
    System.out.println(tasks);
    }

The result here is:

jobo.1.1.1.1.1.1, jobo.1.1.1.1.1.2, jobo.1.1.1.1.1.3*, jobo.1.1.1.1.2.1, jobo.1.1.1.1.2.2, jobo.1.1.1.1.2.3*, jobo.1.1.1.1.3*, jobo.1.1.1.2.1.1, jobo.1.1.1.2.1.2, jobo.1.1.1.2.1.3*, jobo.1.1.1.2.2.1, jobo.1.1.1.2.2.2, jobo.1.1.1.2.2.3*, jobo.1.1.1.2.3*, jobo.1.1.1.3*, jobo.1.1.2.1.1.1, jobo.1.1.2.1.1.2, jobo.1.1.2.1.1.3*, jobo.1.1.2.1.2.1, jobo.1.1.2.1.2.2, jobo.1.1.2.1.2.3*, jobo.1.1.2.1.3*, jobo.1.1.2.2.1.1, jobo.1.1.2.2.1.2, jobo.1.1.2.2.1.3*, jobo.1.1.2.2.2.1, jobo.1.1.2.2.2.2, jobo.1.1.2.2.2.3*, jobo.1.1.2.2.3*, jobo.1.1.2.3*, jobo.1.1.3*, jobo.1.2.1.1.1.1, jobo.1.2.1.1.1.2, jobo.1.2.1.1.1.3*, jobo.1.2.1.1.2.1, jobo.1.2.1.1.2.2, jobo.1.2.1.1.2.3*, jobo.1.2.1.1.3*, jobo.1.2.1.2.1.1, jobo.1.2.1.2.1.2, jobo.1.2.1.2.1.3*, jobo.1.2.1.2.2.1, jobo.1.2.1.2.2.2, jobo.1.2.1.2.2.3*, jobo.1.2.1.2.3*, jobo.1.2.1.3*, jobo.1.2.2.1.1.1, jobo.1.2.2.1.1.2, jobo.1.2.2.1.1.3*, jobo.1.2.2.1.2.1, jobo.1.2.2.1.2.2, jobo.1.2.2.1.2.3*, jobo.1.2.2.1.3*, jobo.1.2.2.2.1.1, jobo.1.2.2.2.1.2, jobo.1.2.2.2.1.3*, jobo.1.2.2.2.2.1, jobo.1.2.2.2.2.2, jobo.1.2.2.2.2.3*, jobo.1.2.2.2.3*, jobo.1.2.2.3*, jobo.1.2.3*, jobo.1.3*, jobo.2.1.1.1.1.1, jobo.2.1.1.1.1.2, jobo.2.1.1.1.1.3*, jobo.2.1.1.1.2.1, jobo.2.1.1.1.2.2, jobo.2.1.1.1.2.3*, jobo.2.1.1.1.3*, jobo.2.1.1.2.1.1, jobo.2.1.1.2.1.2, jobo.2.1.1.2.1.3*, jobo.2.1.1.2.2.1, jobo.2.1.1.2.2.2, jobo.2.1.1.2.2.3*, jobo.2.1.1.2.3*, jobo.2.1.1.3*, jobo.2.1.2.1.1.1, jobo.2.1.2.1.1.2, jobo.2.1.2.1.1.3*, jobo.2.1.2.1.2.1, jobo.2.1.2.1.2.2, jobo.2.1.2.1.2.3*, jobo.2.1.2.1.3*, jobo.2.1.2.2.1.1, jobo.2.1.2.2.1.2, jobo.2.1.2.2.1.3*, jobo.2.1.2.2.2.1, jobo.2.1.2.2.2.2, jobo.2.1.2.2.2.3*, jobo.2.1.2.2.3*, jobo.2.1.2.3*, jobo.2.1.3*, jobo.2.2.1.1.1.1, jobo.2.2.1.1.1.2, jobo.2.2.1.1.1.3*, jobo.2.2.1.1.2.1, jobo.2.2.1.1.2.2, jobo.2.2.1.1.2.3*, jobo.2.2.1.1.3*, jobo.2.2.1.2.1.1, jobo.2.2.1.2.1.2, jobo.2.2.1.2.1.3*, jobo.2.2.1.2.2.1, jobo.2.2.1.2.2.2, jobo.2.2.1.2.2.3*, jobo.2.2.1.2.3*, jobo.2.2.1.3*, jobo.2.2.2.1.1.1, jobo.2.2.2.1.1.2, jobo.2.2.2.1.1.3*, jobo.2.2.2.1.2.1, jobo.2.2.2.1.2.2, jobo.2.2.2.1.2.3*, jobo.2.2.2.1.3*, jobo.2.2.2.2.1.1, jobo.2.2.2.2.1.2, jobo.2.2.2.2.1.3*, jobo.2.2.2.2.2.1, jobo.2.2.2.2.2.2, jobo.2.2.2.2.2.3*, jobo.2.2.2.2.3*, jobo.2.2.2.3*, jobo.2.2.3*, jobo.2.3*, jobo.3.1.1.1.1.1, jobo.3.1.1.1.1.2, jobo.3.1.1.1.1.3*, jobo.3.1.1.1.2.1, jobo.3.1.1.1.2.2, jobo.3.1.1.1.2.3*, jobo.3.1.1.1.3*, jobo.3.1.1.2.1.1, jobo.3.1.1.2.1.2, jobo.3.1.1.2.1.3*, jobo.3.1.1.2.2.1, jobo.3.1.1.2.2.2, jobo.3.1.1.2.2.3*, jobo.3.1.1.2.3*, jobo.3.1.1.3*, jobo.3.1.2.1.1.1, jobo.3.1.2.1.1.2, jobo.3.1.2.1.1.3*, jobo.3.1.2.1.2.1, jobo.3.1.2.1.2.2, jobo.3.1.2.1.2.3*, jobo.3.1.2.1.3*, jobo.3.1.2.2.1.1, jobo.3.1.2.2.1.2, jobo.3.1.2.2.1.3*, jobo.3.1.2.2.2.1, jobo.3.1.2.2.2.2, jobo.3.1.2.2.2.3*, jobo.3.1.2.2.3*, jobo.3.1.2.3*, jobo.3.1.3*, jobo.3.2.1.1.1.1, jobo.3.2.1.1.1.2, jobo.3.2.1.1.1.3*, jobo.3.2.1.1.2.1, jobo.3.2.1.1.2.2, jobo.3.2.1.1.2.3*, jobo.3.2.1.1.3*, jobo.3.2.1.2.1.1, jobo.3.2.1.2.1.2, jobo.3.2.1.2.1.3*, jobo.3.2.1.2.2.1, jobo.3.2.1.2.2.2, jobo.3.2.1.2.2.3*, jobo.3.2.1.2.3*, jobo.3.2.1.3*, jobo.3.2.2.1.1.1, jobo.3.2.2.1.1.2, jobo.3.2.2.1.1.3*, jobo.3.2.2.1.2.1, jobo.3.2.2.1.2.2, jobo.3.2.2.1.2.3*, jobo.3.2.2.1.3*, jobo.3.2.2.2.1.1, jobo.3.2.2.2.1.2, jobo.3.2.2.2.1.3*, jobo.3.2.2.2.2.1, jobo.3.2.2.2.2.2, jobo.3.2.2.2.2.3*, jobo.3.2.2.2.3*, jobo.3.2.2.3*, jobo.3.2.3*, jobo.3.3*, jobo.4*

Anyone knows how this can be simplified and controlled by a variable int depth=123; for example?

Thanks

3 Answers

This option avoids recursion, and simply counts as you would when deciding the next element in the sequence:

private static class LevelGenerator implements Iterator<String> {
    private int[] current;  // min,min,min => min,min,min+1 => ... max,max,max
    private int min, max;   // at each position in current[] array
    private String next;    // to be returned when next() is called

    public LevelGenerator(int levels, int min, int max) {
        this.current = new int[levels];
        for (int i=0; i<levels; i++) this.current[i] = min;
        this.next = output();
        this.min = min;
        this.max = max;
    }

    /** 
     * Int array to string
     */
    private String output() {
        StringBuilder sb = new StringBuilder();
        for (int i : current) sb.append("." + i);
        return sb.toString();
    }

    /**
     * Updates current and next
     * counts as a human would: increments the last index that is not yet `max`,
     *    and then places all elements after it to `min`
     */
    private String step() {            
        for (int i=current.length-1; i>=0; i--) {
            if (current[i] < max) { 
                current[i] ++;
                for (int j=i+1; j<current.length; j++) {
                    current[j] = min;
                }
                return output(); // next step is ready
            }
        }
        return null; // no next step
    }

    @Override
    public String next() {
        if (next == null) throw new IllegalStateException("iteration is finished");
        String output = next;
        next = step();
        return output;
    }
    @Override
    public boolean hasNext() {
        return next != null;
    }
}

public static void main(String ... args) {
    LevelGenerator l = new LevelGenerator(3, 1, 4);
    while (l.hasNext()) {
        System.out.println("job" + l.next());
    }
}

The output for this example would be:

job.1.1.1
job.1.1.2
job.1.1.3
job.1.1.4
job.1.2.1
job.1.2.2
job.1.2.3
job.1.2.4
job.1.3.1
job.1.3.2
job.1.3.3
job.1.3.4
job.1.4.1
job.1.4.2
job.1.4.3
job.1.4.4
job.2.1.1
...
job.4.4.4

You can use Recursion (see wiki https://en.wikipedia.org/wiki/Recursion_(computer_science))

for example (draft, not checking)

@Test
public void buildTaskList1() {
    String jobName ="job";
    int depth=5;
    int max=3;
    List<String> tasks = new ArrayList<>();
    for (long i = 1; i <= max; i++) {
        buildTaskListRecursion(max, depth, tasks, jobName + "."+i);
    }
    tasks.add(jobName+"."+(max+1)+"*");
}


public void buildTaskListRecursion(int max,int depth, List<String> tasks, String jobName){
    String last="";
    for (long j = 1; j <= max; j++) {
        if (j==max){
            last="*";
            tasks.add(jobName+"."+j+last);
        }else {
            depth--;
            if(depth > 0) {
                buildTaskListRecursion(max, depth, tasks, jobName+"."+j);
            } else {
                tasks.add(jobName+"."+j);
            }
        }
    }
}

Yes

1. Support indexes

Create a stack of indexes of the size of depth.

2. Handle the levels properly

You need a currentDepth index to know where you were. This is 0 at first. Whenever an item is increased, push a new item to the stack with 0 as index. Whenever an item is going beyond max, pop it from the stack and increment the previous element. When the first element is popped, you completed all the work

3. You will need to logically know where you were.

Alternatively you could generate code

But that's much more complicated.

EDIT

In agreement with Bdzzaid's legitimate request, I will briefly talk about the Composite Design Pattern. First, let's see a diagram from the page he shared with us:

enter image description here

Source: https://dzone.com/articles/composite-design-pattern-in-java-1

That's a good read I can wholeheartedly recommend to future readers. The idea is that we use the composition of very similar components in a tree-like manner. The pattern is applied on a stack in our case, which can be thought about as a tree having a single branch in all cases. The reason this is beneficial to think about in this way is that we might want to add support for multiple loops, maybe even in an assymmetryc manner at some point. Now, the components are the levels/indexes in our case and they, together form a composition of the stack (or, in more general terms, the tree).

This pattern can be reused in many different situations, so it is advisable to get familiar with it if you not already have.

Related