هل هناك أي طريقة للقيام n-مستوى حلقات متداخلة في جافا ؟

StackOverflow https://stackoverflow.com/questions/426878

سؤال

وبعبارة أخرى ، يمكن أن أفعل شيئا مثل

for() {
    for {
       for {
       }
    }
}

إلا 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،...);ولكن من أجل(...) يجب أن تكون محددة.

على NForTest الملف يجب أن تثبت عدة طرق مختلفة لاستخدامها.

الفرضية الأساسية من هذا يجري ببساطة تقدم "المؤشرات" كل بدوره بدلا من استخدام العودية.

ومشكلة تحتاج الى مزيد من المواصفات. ربما العودية سوف تساعدك، ولكن نأخذ في الاعتبار أن العودية هي دائما تقريبا بديلا للتكرار، والعكس بالعكس. ومن المحتمل أن يكون حلقة متداخلة على مستوى 2 يمكن أن تكون كافية لتلبية الاحتياجات الخاصة بك. فقط اسمحوا لنا أن نعرف ما هي المشكلة التي نحاول حلها.

والفكرة الأساسية وراء تداخل الحلقات الضرب.

التوسع في مايكل بير الجواب إذا كان الخارجي for الحلقات هي لا تفعل شيئا سوى السيطرة على العد ، ثم المتداخلة for حلقات أكثر n التهم هي ببساطة طريقة أكثر تعقيدا من بالتكرار على المنتج من التهم واحدة ، 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>, مع واضحة الأسلوب الثالث.منتج من ثلاث قوائم ، ثم يتحقق عن طريق توزيع قائمة functor على نوع المنتج.وبالتالي فإن المنتج من 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());
}

المضي قدما وظيفيا جافا, يمكنك جعل 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());
    }
  };

ثم يمكنك القضاء على حلقة تماما ، وجمع نتائج جميع التطبيقات 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].

والنهج العام أبرع أتمكن من الخروج مع في جافا 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] );
    }
}

وأو في جاوة 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();
}

ويخلق لطيفة متداخلة هيكل عظمي لحلقة ؛-) ليست خطيرة تماما، وأنا على علم بأن الحل عودي كان يمكن أن يكون أكثر أناقة.

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

وهكذا إذا أردت عدد ن من الأعشاش، ويمكن كتابتها باستخدام الانقسام بين 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;
}

في مصلحة الايجاز أنا أضع قانون بلدي هنا:

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top