Java에서 n 수준 중첩 루프를 수행할 수 있는 방법이 있습니까?
문제
즉, 다음과 같은 작업을 수행할 수 있습니까?
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
당신이 정말로 하고 싶은 것이 무엇인지 설명하고 싶을 수도 있습니다.
외부인 경우 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);
}
}
}
이런 식으로 당신은 서로 안에 둥지를 둔 루프의 스택으로 끝납니다.
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, ...)에서 (1, ...); 그러나 A ~ (...)를 지정해야합니다.
그만큼 NForTest
파일은 몇 가지 다른 방법을 사용해야합니다.
이 전제는 재귀를 사용하기보다는 단순히 턴마다 '지수'를 발전시키는 것입니다.
문제는 더 많은 사양이 필요합니다. 어쩌면 재귀는 당신을 도울 것이지만, 재귀는 거의 항상 반복의 대안이며 그 반대도 마찬가지입니다. 2 단계 중첩 루프가 필요에 충분할 수 있습니다. 해결하려는 문제를 알려주십시오.
중첩 루프의 핵심 아이디어는 다음과 같습니다. 곱셈.
Michael Burr의 답변을 확장하면 외부 for
루프는 카운트를 제어하는 것 외에는 아무것도 하지 않고 중첩됩니다. for
루프 오버 n
count는 단순히 단일 값으로 count의 곱을 반복하는 더 복잡한 방법입니다. for
고리.
이제 이 아이디어를 목록으로 확장해 보겠습니다.중첩 루프에서 세 개의 목록을 반복하는 경우 이는 단일 루프로 목록의 곱을 반복하는 더 복잡한 방법일 뿐입니다.그러면 세 목록의 곱을 어떻게 표현합니까?
첫째, 유형의 산물을 표현하는 방법이 필요하다.두 가지 유형의 제품 X
그리고 Y
다음과 같은 일반 유형으로 표현될 수 있습니다. P2<X, Y>
.이는 두 가지 값으로 구성된 값일 뿐입니다. X
, 다른 유형 Y
.다음과 같습니다.
public abstract class P2<A, B> {
public abstract A _p1();
public abstract B _p2();
}
세 가지 유형의 제품에 대해 우리는 P3<A, B, C>
, 분명한 세 번째 방법이 있습니다.그러면 세 개의 목록으로 구성된 제품은 제품 유형에 대해 목록 기능자를 배포하여 달성됩니다.그래서 제품은 List<X>
, List<Y>
, 그리고 List<Z>
단순히 List<P3<X, Y, Z>>
.그런 다음 단일 루프를 사용하여 이 목록을 반복할 수 있습니다.
그만큼 기능적 자바 도서관에는 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);
다음과 같은 일반적인 중첩 루프 구조를 가지고있는 경우 :
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]
.
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);
}
}
}
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-loop 골격을 만듭니다 ;-) 완전히 심각하지 않으며 재귀 솔루션이 더 우아했을 것임을 알고 있습니다.
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);
질문에 처음으로 대답했지만`의이 정보를 공유해야한다고 생각했습니다.
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
이것은 저에게 정말 친절하게 작동했습니다. 나는 일부 대안 중에서 선택해야했는데, 근거리 경로에 저장된 대안 중에서 선택해야했고 기본 아이디어는 다음 선택을 구성하려고했고, 한 차원 / 구성 요소에 "오버플로"가 있었을 때, 당신은 단지 당신은 단지 당신은 단지 당신입니다. 해당 차원을 다시 구분하고 다음 차원에 추가하십시오.
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;
}
간결함을 위해 나는 여기에 내 코드를 넣고있다.
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