【问题标题】:Change order of for loops?改变for循环的顺序?
【发布时间】:2013-03-21 09:14:27
【问题描述】:

我有一种情况,我需要根据用户输入以不同的顺序循环 xyz 坐标。所以我是 3D 空间中的一个区域,然后是一组这样的 for 循环。

for(int x = 0; x < build.getWidth(); x++){
   for(int y = 0; y < build.getHeight(); y++){
     for(int z = 0; z < build.getLength(); z++){
        //do stuff
       }
    }
 }

但是根据用户的输入,顺序可能是这样的。

for(int z = 0; z < build.getLenght(); z++){
   for(int y = 0; y < build.getHeight(); y++){
     for(int x = 0; x < build.getWidth(); x++){
        //do stuff
       }
    }
 }

甚至是负面的。

for(int x = build.getWidth(); x > 0; x--){
   for(int y = 0; y < build.getHeight(); y++){
      for(int z = 0; z < build.getLength(); z++){
        //do stuff
      }
   }
}

有没有什么方法可以在不对每个案例都进行硬编码的情况下做到这一点?

【问题讨论】:

  • 使用迭代器。您的程序被硬编码为以iter1、iter2、iter3 进行迭代,但在进入循环之前,请根据用户输入进行设置。
  • 这可行。我只需要为 xyz 坐标设置一些集合,如果我需要在相反的方向上反转它们。
  • @aioobe 如果这是一个答案,我会投赞成票的!

标签: java loops dynamic 3d coords


【解决方案1】:

这是一个 n 维步进器,它可以从任何起始位置到任何限制以任何顺序步进任意数量的维度。示例见测试代码。

public class Test {
  public void test() {
    int[] limits = {3, -5, 7};
    int[] order = {0, 2, 1};
    int[] starts = {0, 0, 0};
    int[] steps = {1, -1, 2};
    NDimensionalStepper nds = new NDimensionalStepper(limits, order, starts, steps);
    do {
      System.out.println(nds);
    } while (nds.step());
  }

  public static void main(String args[]) {
    new Test().test();
  }

  public static class NDimensionalStepper {
    // The current positions in each dimension.
    // Note that i[order[0]] is the fastest mover.
    final int[] i;
    // Starts.
    final int[] starts;
    // Steps.
    final int[] steps;
    // Limits.
    final int[] limits;
    // Order.
    final int[] order;
    // The (unordered) dimension we last stepped.
    int d = 0;

    // Full constructor.
    public NDimensionalStepper(int[] limits, int[] order, int[] starts, int[] steps) {
      // Should parameter check to ensure all are the same length.
      // Should also check that each dimension will terminate.
      this.i = Arrays.copyOf(starts, starts.length);
      this.starts = Arrays.copyOf(starts, starts.length);
      this.steps = Arrays.copyOf(steps, steps.length);
      this.limits = Arrays.copyOf(limits, limits.length);
      this.order = Arrays.copyOf(order, order.length);
    }

    // Default steps to 1.
    public NDimensionalStepper(int[] limits, int[] order, int[] starts) {
      this(limits, order, starts, defaultSteps(limits, starts));
    }

    // Default steps - 1 Towards limits.
    private static int[] defaultSteps(int[] limits, int[] starts) {
      int[] steps = new int[limits.length];
      for (int i = 0; i < limits.length; i++) {
        // Step towrds limits.
        steps[i] = (int) Math.signum(limits[i] - starts[i]);
      }
      return steps;
    }

    // Default starts to 0.
    public NDimensionalStepper(int[] limits, int[] order) {
      this(limits, order, defaultStarts(limits.length));
    }

    // Default starts - 0, 0, ...
    private static int[] defaultStarts(int d) {
      int[] starts = new int[d];
      Arrays.fill(starts, 0);
      return starts;
    }

    // Default order to normal.
    public NDimensionalStepper(int[] limits) {
      this(limits, defaultOrder(limits.length));
    }

    // Default order - ..., 1, 0
    private static int[] defaultOrder(int d) {
      int[] order = new int[d];
      for (int i = 0; i < d; i++) {
        order[i] = d - i - 1;
      }
      return order;
    }

    // Get the current position in dimension d.
    public int get(int d) {
      return i[d];
    }

    // Take just one step. Return false if cant.
    public boolean step() {
      boolean stepped = false;
      boolean finished = false;
      while (!stepped && !finished) {
        // Which dimension should be stepped (depends on order).
        int o = order[d];
        // Can we step in the current dimension?
        while (finished(o) && d < order.length - 1) {
          // Reached a limit! - Move up one dimension.
          o = order[++d];
        }
        if (d < order.length && !finished(o)) {
          // Step it.
          i[o] += steps[o];
          stepped = true;
          // Zero all lower dimensions.
          while (d > 0) {
            d -= 1;
            i[order[d]] = starts[order[d]];
          }
        } else {
          // Got to the last without finding one below limit. Finished!
          finished = true;
        }
      }
      return !finished;
    }

    // Equal or passed the limits.
    private boolean finished(int o) {
      int sign = (int) Math.signum(steps[o]);
      return sign * (i[o] + steps[o]) >= sign * limits[o];
    }

    @Override
    public String toString() {
      StringBuilder s = new StringBuilder();
      s.append("{");
      for (int d = 0; d < order.length; d++) {
        s.append(get(d));
        if (d < order.length - 1) {
          s.append(",");
        }
      }
      s.append("}");
      return s.toString();
    }
  }
}

我对您的三个场景的等效测试如下所示:

  private void testBuild1(Build build) {
    System.out.println("Build: x,y,z");
    for (int x = 0; x < build.getWidth(); x++) {
      for (int y = 0; y < build.getHeight(); y++) {
        for (int z = 0; z < build.getLength(); z++) {
          System.out.println("{" + x + "," + y + "," + z + "}");
        }
      }
    }
    int[] limits = {build.getWidth(), build.getHeight(), build.getLength()};
    testNDS(new NDimensionalStepper(limits));
  }

  private void testBuild2(Build build) {
     System.out.println("Build: z,y,x");
    for (int z = 0; z < build.getLength(); z++) {
      for (int y = 0; y < build.getHeight(); y++) {
        for (int x = 0; x < build.getWidth(); x++) {
          System.out.println("{" + x + "," + y + "," + z + "}");
        }
      }
    }
    int[] limits = {build.getWidth(), build.getHeight(), build.getLength()};
    int[] order = {0,1,2};
    testNDS(new NDimensionalStepper(limits, order));
  }

  private void testBuild3(Build build) {
    System.out.println("Build: x--,y,z");
    for (int x = build.getWidth(); x > 0; x--) {
      for (int y = 0; y < build.getHeight(); y++) {
        for (int z = 0; z < build.getLength(); z++) {
          System.out.println("{" + x + "," + y + "," + z + "}");
        }
      }
    }
    int[] limits = {0, build.getHeight(), build.getLength()};
    int[] order = {2,1,0};
    int[] starts = {build.getWidth(), 0, 0};
    int[] steps = {-1, 1, 1};
    testNDS(new NDimensionalStepper(limits, order, starts, steps));
  }

  private void testNDS(NDimensionalStepper nds) {
    System.out.println("--nds--");
    do {
      System.out.println(nds);
    } while (nds.step());
  }

【讨论】:

    【解决方案2】:

    您说循环更改的顺序取决于用户输入。必须编写处理用户输入的逻辑。

    你可以这样编码:

    //Code to populate XInit, XEnd, YInit, YEnd, ZInit, ZEnd based on user input
    
        for(int x = XInit; x < XEnd; x=XInit<XEnd?x+1:x-1){
           for(int y = YInit; y < YEnd; y=YInit<YEnd?y+1:y-1){
             for(int z = ZInit; z < ZEnd; z=ZInit<ZEnd?z+1:z-1){
                //do stuff
               }
            }
         }
    

    注意:您甚至可能希望将 XInit、XEnd 等参数的计算抽象到一个单独的方法中。

    【讨论】:

    • 你能给出完整的答案吗? XInit, XEnd, YInit, YEnd, ZInit, ZEnd 对于 case1、case2 和 case3 应该有哪些值?
    • 我没有指定,因为它取决于应用程序使用的逻辑。问题中并不清楚用户输入和迭代顺序之间的关系。
    【解决方案3】:

    您的“东西”可能会访问 x、y 和 z 的值,因此您进行硬编码的方式可能是最容易遵循的。您的方法名称可以清楚地表明顺序。对于您给出的三个示例,它看起来类似于:

    public void somethingXYZ(Build build, Stuff stuff) {...}
    public void somethingZYX(Build build, Stuff stuff) {...}
    public void somethingXnYZ(Build build, Stuff stuff) {...}
    

    当您编写代码并想要选择其中一种方法时,您的 IDE 甚至会通过列出该类的可用选项来帮助您。我认为你组织它的方式已经很好了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 2021-03-06
      • 2021-06-29
      • 2017-03-04
      相关资源
      最近更新 更多