つまり、次のようなことができますか
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
ファイルは、それを使用するいくつかの異なる方法を示す必要があります。
これの基本的な前提は、再帰を使用するのではなく、各ターンの「インデックス」を単純に進めることです。
先日、実際に考えていました。
おそらく完璧ではありませんが、私が尋ねられていると思うものにかなり近い例は、ディレクトリツリーを印刷することです
public void printTree(directory) {
for(files in directory) {
print(file);
if(file is directory) {
printTree(file);
}
}
}
このようにして、forループのスタックが互いにネストされ、それらがどのように連携するかを正確に理解する手間がかかりません。
本当にやりたいことを説明したいと思うかもしれません。
外側の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();
}
ネスト ループの背後にある本質的な考え方は乗算です。
Michael Burr の答えを拡張すると、外側のfor
ループがカウントを制御する以外に何もしない場合、カウントに対するネストされたfor
ループn
は、単一のループでカウントの積を反復処理する単純な方法ですfor
。
では、このアイデアをリストに拡張しましょう。入れ子になったループで 3 つのリストを反復処理している場合、これは単に 1 つのループでリストの積を反復処理するより複雑な方法です。しかし、3 つのリストの積をどのように表現しますか?
まず、型の積を表現する方法が必要です。2 つの型の積 と のX
ようY
なジェネリック型として表現できますP2<X, Y>
。X
これは、2 つの値 (1 つは type 、もう 1 つは type )から構成される単なる値ですY
。次のようになります。
public abstract class P2<A, B> {
public abstract A _p1();
public abstract B _p2();
}
3 つのタイプの製品の場合P3<A, B, C>
、明らかな 3 番目の方法を使用して、 が得られます。3 つのリストの積は、リスト ファンクタを積の型に分散することによって実現されます。したがって、、、およびの積List<X>
はList<Y>
単純にList<Z>
ですList<P3<X, Y, Z>>
。その後、単一のループでこのリストを反復処理できます。
Functional Javaライブラリには、ファーストクラスの関数と積の型 (ライブラリにも含まれる P2、P3 など) を使用して乗算リストを一緒にサポートする型List
があります。
例えば:
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);
問題にはさらに仕様が必要です。再帰が役立つかもしれませんが、再帰はほとんどの場合反復の代替手段であり、その逆も同様であることに注意してください。ニーズには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");
}
counters は size の配列で、それぞれ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);
}
}
}
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
簡潔にするために、ここにコードを配置しています。
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