有没有办法在Java中进行n级嵌套循环?

时间:2009-01-09 02:38:31

标签: java recursion loops for-loop

换句话说,我可以做点什么吗

for() {
    for {
       for {
       }
    }
}

除了N次?换句话说,当调用创建循环的方法时,会给它一些参数N,然后该方法会创建N个嵌套在另一个中的N?

当然,我们的想法是应该采用“简单”或“通常”的方式。我已经有了一个非常复杂的想法。

16 个答案:

答案 0 :(得分:33)

jjnguy是对的;递归可让您动态创建可变深度嵌套。但是,如果没有更多工作,您无法访问外层数据。 “嵌入式嵌套”案例:

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

将变量ijk保留在最内层正文的范围内。

这是一个快速破解的方法:

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

IAction接口规定了受控操作的作用,该操作将索引数组作为其act方法的参数。

在此示例中,NestedFor的每个实例都由构造函数配置,其中包含迭代限制以及要由最内层执行的操作。 nFor方法的参数指定嵌套的深度。

以下是一个示例用法:

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

和执行的(部分)输出:

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

答案 1 :(得分:20)

听起来您可能想要查看recursion.

答案 2 :(得分:6)

你可能想解释一下你真正想做的事情。

如果外部for循环除了控制计数之外什么都不做,那么嵌套的for循环只是一种更复杂的迭代方式,可以通过单个{{1循环。

例如:

for

相当于:

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

答案 3 :(得分:5)

前几天我正在考虑这件事。

一个可能不完美但与我认为被问到的非常接近的例子是打印出一个目录树

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

这样你最终会将一堆for循环嵌套在彼此之内,而不必麻烦地弄清楚它们应该如何组合在一起。

答案 4 :(得分:5)

2015编辑:与前一个咒语一样,我做了以下包来处理这个问题。 https://github.com/BeUndead/NFor

用法如下

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

导致

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

它还支持lessThan以外的条件。使用情况(使用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));

导致:

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

显然,支持不同长度和不同类(所有盒装,数字基元)的循环。默认值(如果未指定)来自(0,...)。by(1,...);但必须指定一个到(...)。

NForTest文件应该演示几种不同的使用方法。

这个基本前提是每次转向简单地推进'指数',而不是使用递归。

答案 5 :(得分:3)

问题需要更多规范。也许递归会对你有所帮助,但请记住,递归几乎总是迭代的替代方法,反之亦然。可能是2级嵌套循环足以满足您的需求。请告诉我们您要解决的问题。

答案 6 :(得分:3)

嵌套循环背后的基本思想是乘法

扩展Michael Burr的答案,如果外部for循环除了控制计数之外什么都不做,那么嵌套for循环超过n计数只是一种更复杂的迭代方式使用单个for循环进行计数的乘积。

现在,让我们将这个想法扩展到列表。如果您在嵌套循环中迭代三个列表,这只是一种使用单个循环迭代列表产品的更复杂方法。但是,您如何表达三个列表的产品?

首先,我们需要一种表达类型产品的方法。 XY两种类型的产品可以表示为类似P2<X, Y>的通用类型。这只是一个由两个值组成的值,一个是X类型,另一个是类型Y。它看起来像这样:

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

对于三种类型的产品,我们只有P3<A, B, C>,显而易见的第三种方法。然后,通过在产品类型上分发List仿函数来实现三个列表的产品。因此,List<X>List<Y>List<Z>的产品只是List<P3<X, Y, Z>>。然后,您可以使用单个循环遍历此列表。

Functional Java库具有List类型,支持使用一流函数和产品类型(P2,P3等,也包含在库中)将列表相乘。

例如:

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

相当于:

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

进一步使用Functional Java,您可以使doSomething成为一流的,如下所示。假设doSomething返回一个字符串:

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

然后你可以完全消除for循环,并收集doSomething的所有应用程序的结果:

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

答案 7 :(得分:2)

如果你有一个通用的嵌套循环结构,如:

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是循环变量,d是嵌套循环的深度。

等效递归解决方案:

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

计数器是一个大小为d的数组,分别代表相应的变量i0,i1,i2,...id int counters[d]

nestedToRecursion(counters,0);

类似地,我们可以转换其他变量,如初始化递归或结束使用数组,即我们可以initial[d], ending[d]

答案 8 :(得分:1)

我在Java 7中提出的最常用的方法是

$(function(){
$('#content').each(function() {
    var text = $(this).html();
    console.log(text);
    console.log(stripHTML(text));
    if(stripHTML(text).indexOf("My Company")>-1)
    {
        alert("FOUND");
        $(this).html($(this).html() + "&reg;");
    }


});
function stripHTML(dirtyString) {
    var container = document.createElement('div');
    container.innerHTML = dirtyString;
    return container.textContent;
}
});

或者在Java 8中:

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

支持此功能的实现是:

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

答案 9 :(得分:1)

基于流的 Java 8 解决方案:

public static Stream<int[]> nest(Supplier<IntStream> first, Supplier<IntStream>... streams) {
    Stream<int[]> result = first.get().mapToObj(i -> new int[]{i});

    for (Supplier<IntStream> s : streams) {
        result = nest(result, s);
    }

    return result;
}

private static Stream<int[]> nest(Stream<int[]> source, Supplier<IntStream> target) {
    return source.flatMap(b -> target.get().mapToObj(i -> {
        int[] result = new int[b.length + 1];
        System.arraycopy(b, 0, result, 0, b.length);
        result[b.length] = i;
        return result;
    }));
}

另一个不是线程安全的,但避免额外的副本:

public static Stream<int[]> nest(Supplier<IntStream>... streams) {
    final int[] buffer = new int[streams.length];
    Stream<int[]> result = Stream.of(buffer);

    for (int n = 0; n < streams.length; n++) {
        result = nest(result, streams[n], n);
    }

    // Might need to perform a copy here, if indices are stored instead of being consumed right away.
    // return result.map(b -> Arrays.copyOf(b, b.length));
    return result;
}

private static Stream<int[]> nest(Stream<int[]> source, Supplier<IntStream> target, int index) {
    return source.flatMap(b -> target.get().mapToObj(i -> {
        b[index] = i;
        return b;
    }));
}

用法:

nest(
    () -> IntStream.range(0, 2),
    () -> IntStream.range(0, 2), 
    () -> IntStream.range(0, 3))
    .forEach(indices -> System.out.println( Arrays.toString(indices)));

输出:

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

答案 10 :(得分:0)

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

创建一个漂亮的嵌套for循环骨架;-) 不完全严重,我知道递归解决方案会更优雅。

答案 11 :(得分:0)

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

示例电话:

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

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

答案 12 :(得分:0)

我第一次回答问题,但我觉得我需要分享这些信息 的 `

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

等同于

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

所以,如果您想要n个巢,可以使用baseloop之间的划分来编写,这样看起来就像这样简单:

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

因此,如果您要输入printer(10, 2);,则会打印出来:

00
01
02
03
04
...
97
98
99

答案 13 :(得分:0)

这对我来说真的很不错-我不得不从存储在myAlternativePaths中的一些替代项中进行选择,其基本思想是我试图构造下一个选择项,并且在一个维度/组件中出现“溢出”时,您只需重新初始化该尺寸并将其添加到下一个尺寸即可。

library(tidyverse)
NBA%>%
  group_by(season)%>%
  nest() %>%
  mutate(data = map(data, ~ .x %>%
        group_by(player)%>%
        summarise(shots_attempts_ratio=sum(shot_made)/n(),
        total_attempts=n(),
        shots=sum(shot_made))
        )) %>%
 unnest

答案 14 :(得分:0)

我也试图解决这个问题,并最终创建了这个简单的solution

例如,假设我们需要动态生成这样的循环:

for (int i = 0; i < 2; i++) {
    for (int j = 1; j < 3; j++) {
        for (int k = 2; k < 4; k++) {
            System.out.println(Arrays.asList(i, j, k));
        }
    }
}

因此,我们可以使用这样的构建器来实现它:

new Loops()
    .from(0).to(2)
    .from(1).to(3)
    .from(2).to(4)
    .action(System.out::println);

执行结果:

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

我希望它对其他人也有用。

答案 15 :(得分:-1)

为了简洁起见,我将代码放在这里:

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

输出

 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