换句话说,我可以做类似的事情吗
for() {
for {
for {
}
}
}
除了N次?换句话说,当调用创建循环的方法时,给它一些参数N,然后该方法会创建N个这些循环嵌套在另一个?
当然,这个想法是应该有一种“简单”或“通常”的方式来做这件事。我已经有了一个非常复杂的想法。
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**
}
}
}
将变量i
、j
和保留k
在最里面的主体使用的范围内。
这是一个快速的技巧:
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
听起来您可能想研究递归。
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]
显然,支持不同长度和不同类别(所有装箱的数字基元)的循环。默认(如果未指定)是 from(0, ...).by(1, ...); 但必须指定 to(...)。
该NForTest
文件应演示几种不同的使用方式。
这样做的基本前提是每轮简单地推进“索引”而不是使用递归。
你可能想解释你真正想做的事情。
如果外部for
循环除了控制计数什么都不做,那么嵌套for
循环只是一种更复杂的迭代计数的方法,可以由单个for
循环处理。
例如:
for (x = 0; x < 10; ++x) {
for (y = 0; y < 5; ++y) {
for (z = 0; z < 20; ++z) {
DoSomething();
}
}
}
相当于:
for (x = 0; x < 10*5*20; ++x) {
DoSomething();
}
前几天我真的在想这个。
一个可能不完美但与我认为被问到的非常接近的示例是打印目录树
public void printTree(directory) {
for(files in directory) {
print(file);
if(file is directory) {
printTree(file);
}
}
}
这样,您最终会得到一堆嵌套在彼此内部的 for 循环,而无需弄清楚它们应该如何组合在一起。
嵌套循环背后的基本思想是乘法。
扩展 Michael Burr 的回答,如果外部for
循环除了控制计数之外什么都不做,那么计数的嵌套for
循环n
只是用单个for
循环迭代计数乘积的更复杂的方法。
现在,让我们将这个想法扩展到列表。如果您在嵌套循环中迭代三个列表,这只是用单个循环迭代列表乘积的一种更复杂的方法。但是如何表达三个列表的乘积呢?
首先,我们需要一种表示类型乘积的方法。两种类型的乘积,X
可以Y
表示为泛型类型,如P2<X, Y>
. 这只是一个由两个值组成的值,一个是 type X
,另一个是 type 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());
}
进一步使用函数式 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);
问题需要更多规范。也许递归会帮助你,但请记住,递归几乎总是迭代的替代方案,反之亦然。2 级嵌套循环可能足以满足您的需求。只需让我们知道您要解决的问题。
我在 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] );
}
}
或者在 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]; }
);
支持这一点的实现是:
/**
* 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);
}
}
}
如果您有一个通用的嵌套循环结构,例如:
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");
}
counters是一个大小数组,分别d
代表对应的变量。i0,i1,i2,...id
int counters[d]
nestedToRecursion(counters,0);
类似地,我们可以转换其他变量,如递归的初始化或使用数组结束,即我们可以有initial[d], ending[d]
.
基于流的 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]
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);
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 循环骨架 ;-) 不是很严重,我知道递归解决方案会更优雅。
我第一次回答问题,但我觉得我需要分享这个信息
for (x = 0; x < base; ++x) {
for (y = 0; y < loop; ++y) {
DoSomething();
}
}
相当于
for (x = 0; x < base*loop; ++x){
DoSomething();
}
因此,如果您想要 n 个嵌套,可以使用除法来编写base
,loop
因此它看起来像这样简单:
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
这对我来说真的很好 - 我必须从存储在 myAlternativePaths 中的一些替代方案中进行选择,基本思想是我试图构建下一个选择,并且当一个维度/组件中出现“溢出”时,你只需重新初始化该维度并将一个添加到下一个。
public boolean isValidAlternativeSelection (int[] alternativesSelected) {
boolean allOK = true;
int nPaths= myAlternativePaths.size();
for (int i=0; i<nPaths; i++) {
allOK=allOK & (alternativesSelected[i]<myAlternativePaths.get(i).myAlternativeRoutes.size());
}
return allOK;
}
public boolean getNextValidAlternativeSelection (int[] alternativesSelected) {
boolean allOK = true;
int nPaths= myAlternativePaths.size();
alternativesSelected[0]=alternativesSelected[0]+1;
for (int i=0; i<nPaths; i++) {
if (alternativesSelected[i]>=myAlternativePaths.get(i).myAlternativeRoutes.size()) {
alternativesSelected[i]=0;
if(i<nPaths-1) {
alternativesSelected[i+1]=alternativesSelected[i+1]+1;
} else {
allOK = false;
}
}
// allOK=allOK & (alternativesSelected[i]<myAlternativePaths.get(i).myAlternativeRoutes.size());
}
return allOK;
}
我也试图解决这个问题,最终创造了这个简单的解决方案。
例如,假设我们需要动态生成这样的循环:
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]
我希望它对其他人也有用。
如果您想处理数据而不仅仅是数字。可以尝试以下解决方案:
class WordGenerator {
//My custom spell checker.
//It returns an empty string if the word is not there in dictionary
public static MySpellChecker spellCheck;
public static void main(String args[]) throws Exception {
spellCheck = new MySpellChecker();
List<String> consonants = List.of("c","t");
List<String> vowels = List.of("a","o","");
//adding to this list will increase nesting
List<List<String>> input = new ArrayList<>();
input.add(consonants);
input.add(vowels);
input.add(vowels);
input.add(consonants);
MyForLoop fLoop = new MyForLoop(input.listIterator(), //for nesting loops
new ArrayList<String>(), //loop state
//action to perform in innermost loop
(state)->spellCheck.check(String.join("", state)));
//start execution
fLoop.accept("");
//print results
System.out.println("No of iterations: " + fLoop.getResult().size());
System.out.println("\nFound words: " + String.join(", ", fLoop.getResult()));
}
}
class MyForLoop implements Consumer<String> {
private static List<String> result = new ArrayList<>();
private ListIterator<List<String>> itr;
private Function<List<String>, String> action;
private List<String> state = new ArrayList<>();
public MyForLoop(ListIterator<List<String>> itr, List<String> collected, Function<List<String>, String> action) {
this.itr = itr;
this.action = action;
state = new ArrayList<>(collected);
}
@Override
public void accept(String s) {
if(!s.isBlank())
state.add(s);
if(itr.hasNext()) {
itr.next().stream().forEach(new MyForLoop(itr, state, action));
if(!state.isEmpty())
state.remove(state.size()-1);
itr.previous();
} else {
result.add(action.apply(state));
state.remove(state.size()-1);
}
}
public static List<String> getResult() {
return result;
}
}
输出:
No of iterations: 36
Found words: , , , , , cat, , coat, , coot, , cot, , , , , , , , , , , , tat, , toat, , toot, toc, tot, , , , , ,
这里的代码生成以'c'和't'开头和结尾的单词,它们之间最多有两个元音。您更改列表中的顺序input
以更改单词创建。
该MyForLoop
对象维护一个state
列表,该列表保存当前的迭代状态。状态列表的每个元素给出嵌套 for 循环的相应级别的状态。
状态可用于action
,在最内层循环中执行。
为了使演示代码简短明了,我偷工减料。它可以通过多种方式进行改进。
我使用 Jazzy 库来检查拼写。
<dependency>
<groupId>net.sf.jazzy</groupId>
<artifactId>jazzy</artifactId>
<version>0.5.2-rtext-1.4.1-2</version>
</dependency>
为了简洁起见,我将代码放在这里:
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