Реверсивные петли - PullRequest
       7

Реверсивные петли

1 голос
/ 03 ноября 2010

Я экспериментировал с игрой, которую пытаюсь сделать.Я обнаружил, что у меня есть два метода, которые были идентичны, за исключением цикла for, который был просто обратным предыдущему.

Я попытался сделать так, чтобы я мог использовать один и тот же код вперед или назад.Я закончил с:

for (int i = start; i != (finish + 1 * ((start < finish) ? 1 : -1)); i += 1 * ((start < finish) ? 1 : -1))

A) B) Я хотел бы поделиться этой концепцией со всеми.

B) Мне любопытно, насколько эффективен такой цикл.Я знаю, что это вычисляет целевое число и коэффициент инкримента при обращении, но я не знаю, как это проверить.

C) Пока я в нем, я нашел одну строку, которая меняет местами две переменные безиспользуя временный.Чтобы прочитать, требуется время, но лучше ли это (с точки зрения кода), чем использовать temp?(3-я строка в основном ниже)

Я проверил его на функционирование, и он работает, как и ожидалось.Если второе число больше первого, оно считается.Если нет, то обратный отсчет.Мой тестовый код был:

// A generic loop that can go up or down
import java.io.*;

public class reversable
{
  public static int start = 1;
  public static int finish = 10;

   public static void main(String[] args)
  {
     for (int i = start; i != (finish + 1 * ((start < finish) ? 1 : -1)); i += 1 * ((start < finish) ? 1 : -1))
     {
        System.out.println("i = " + i);
     }
     finish = (start + finish) - (start = finish);  
System.out.println("Finish = " + finish);
System.out.println("Start = " + start);
     for (int i = start; i != (finish + 1 * ((start < finish) ? 1 : -1)); i += 1 * ((start < finish) ? 1 : -1))
     {
        System.out.println("i = " + i);
     }
     finish = 10;
     for (int i = start; i != (finish + 1 * ((start < finish) ? 1 : -1)); i += 1 * ((start < finish) ? 1 : -1))
     {
        System.out.println("i = " + i);
     }
  }
}

Исходя из комментариев, это будет приемлемо:

public static void reversable (int i, int j)
{
  if (i > j) int inc = -1;  // Count down
  else       int inc = 1;  // Count up
  j += inc;
  for (i; i != j; i += inc)
  {
    dostuff();
    morestuff();
    mostuff();
  }
}

Ответы [ 4 ]

15 голосов
/ 03 ноября 2010

Вы должны стремиться писать читаемый код, а не код, который делает много вещей в одной строке или имеет причудливые трюки.

Если бы я работал с вами над проектом, я бы, вероятно, хотел задушить вас после того, как я впервые прочитал этот цикл for, то есть, если бы моя голова не взорвалась, пытаясь понять это в первую очередь. То же самое касается назначения в назначении.

10 голосов
/ 03 ноября 2010

Если бы я увидел этот код в производстве, я бы сразу же преобразовал его в нечто более читабельное, что, вероятно, было бы чем-то вроде того, с чего вы начали.

Подумайте о причине , которую вы хотитеуменьшить дублирование кода.Это сделать ваш код более понятным.Вы чувствуете, что сделали это?

Пока я в этом, я нашел одну строку, которая меняет две переменные без использования временного.Чтение занимает минуту, но лучше ли это (с точки зрения кода), чем использовать temp?

Это работает только с числовыми типами, поэтому я не считаю это полезным.Вы сделали свой код менее читаемым для тех, кто не знает хитрости, что замедляет обслуживание кода.Я не думаю, что стоит экономить одну временную переменную.

6 голосов
/ 03 ноября 2010

Хм, не пытаясь быть кирпичом, но я думаю, что вы пытались написать:

public static void doAction(int i) {
    System.out.println("i = " + i);
}

public static void loopValues(int i, int j) {
    if (i > j) while (i >= j) doAction(i--);
    else       while (i <= j) doAction(i++);;
}

public static void main(String[] args) {
    int start = 1, finish = 10;
    loopValues(start, finish);
    loopValues(finish, start);
}

Причина, по которой у вас есть 2 цикла, - это эффективность и удобство чтения.Сравнение является дорогостоящей операцией, и вы, как правило, не хотите добавлять дополнительные сравнения в цикл, просто чтобы изменить его нормальный ход.

0 голосов
/ 03 ноября 2010

Действительно, за кодом трудно следовать ... но этого достаточно для критиков. Когда кто-то спрашивает о методах многократного использования, где отличается только цикл, единственное, что приходит на ум - это Iterator. шаблон итератора - это именно то, что вам нужно для этого многоразового использования. Если вы поместите свой итератор в интерфейс Iterable , то вы можете легко использовать его внутри блока for. Пример:

public class IntegerRange implements Iterable<Integer> {
   private boolean reverse;
   private int start;
   private int end;

   public IntegerRange(int start, int end) {
      this.reverse = (start > end);
      this.start = start;
      this.end = end;
   }

   @Override
   public Iterator<Integer> iterator() {
      return new IntegerIterator();
   }


   private class IntegerIterator implements Iterator<Integer> {

      private int current;

      private IntegerIterator() {
         current = start;
      }

      @Override
      public boolean hasNext() {
         if (reverse) {
            return (end <= current);
         } else {
            return (current <= end);
         }
      }

      @Override
      public Integer next() {
         if (!hasNext()) {
            throw new NoSuchElementException();
         }
         if (reverse) {
            return current--;
         } else {
            return current++;
         }
      }

      @Override
      public void remove() {
         throw new UnsupportedOperationException("Cannot remove from this iterator");
      }
   }

}

Затем используйте и снова используйте свой итератор ...

static public void main(String...args) {
   doStuff(new IntegerRange(1, 10));
   doStuff(new IntegerRange(10, 1));
}

static private void doStuff(IntegerRange range) {
   for (int i : range) {
      System.out.println("i = " + i);
   }
} 

Код теперь более читабелен.

...