Что возможно в цикле - PullRequest
16 голосов
/ 02 июня 2011

Итак, сегодня я пошел на собеседование, и один из вопросов был следующий (контекст C #).

//Print the output for the following code:
for (int i = 10, j = 0; j <= 10; j++, i--)
{
    if (i > j)
        Console.WriteLine(j.ToString());
}

Я никогда раньше не видел такой конструкции, и, спросив моих коллег, 4 из 5 на моем рабочем месте тоже не знали (возможно, больше размышлений о нас, но я отвлекся) . Используя некоторую базовую логику, я смог правильно ответить на вопрос, но это знание радикально изменило мое понимание того, как можно структурировать циклы.

Так что я думаю, что мой вопрос сводится к этому.

  1. Все ли языки на основе синтаксиса C реализуют эту функцию? IE: C, C ++, Java, JavaScript и т. Д.
  2. Откуда исходит этот синтаксис?
  3. Существуют ли другие "не очень известные" структуры, которые может принимать цикл for?
  4. Является ли написание кода, подобного описанному выше, плохой практикой, учитывая, как трудно читать?
  5. Существуют ли хорошие примеры из реальной жизни, когда такая структура требуется ?

Ответы [ 8 ]

25 голосов
/ 02 июня 2011
for (statement1; statement2; statement3)
{
     /* body */
}

(1) Сначала выполняется statement1.

(2) Далее statement2 выполняется.

(3) Если оценка statement2 истинна, то тело выполняется

(4) Затем выполняется statement3.

(5) Повторите с шага (2)

          |                  +<-----------------+
          |                  |                  ^
          V                  V                  |
 for (  (s1); -------->(s2 true? | false?);    (s3) )
 {                           |       |          ^
                             |       |          |
                             |       |          |
                             V       |          |
                          (body)-----|--------->+
 }                                   |
                                     |
                                     V
                                 (come out)

Структура, которую вы показали, такая же, как и выше. statement n может быть любым утверждением. В вашем примере вы разделены запятыми в statement1 и statement3. Вы можете разделить любое количество операторов запятыми.

Обычно циклы for используются с statement1 с инициализацией, поскольку она выполняется только один раз. statement2 используется для проверки условия завершения цикла, потому что значение оценки этого оператора используется, чтобы решить, следует ли вводить тело прерывания. И statement3 используется для обновления переменной завершения цикла, так как она выполняется после тела. Но, как правило, они могут быть использованы любым способом.

Сначала statement1 - это i=10, j=0;, это инициализирует переменные. Далее в statement2 стоит j <= 10, если это правда, то тело выполняется. После того, как тело выполнено, выполняется statement3, что составляет i--,j++. Цикл будет повторяться 11 раз 0 до 10. Но напечатает 5 раз, так как в один момент i и j станут одинаковыми, а if (i > j) оценит ложь.

EDIT Вот пример, где его можно использовать, не очень практично, но в качестве примера, для проверки строки палиндрома.

  int i, j, n, flag;
  char str[128];

  printf ("\nEnter string: ");
  scanf ("%s", &str);
  n = strlen (str);


for (flag=1, i=n-1, j=0; j<n/2; j++, i--)
{
  if (str[i] != str[j])
  {
    flag = 0;
    break;
  }
}

if (flag)
 printf ("\n\"%s\" is a palindrome");
else
 printf ("\n\"%s\" is not a palindrome");

Мы всегда должны пытаться написать код, который легко читать и который не создает путаницы. Это помогает как разработчику кода, так и другим, кто читает код.

10 голосов
/ 02 июня 2011
  1. Java и C # имеют одинаковый синтаксис
  2. Синтаксис, вероятно, происходит от C / C ++, который является «корнем» Java & C #
  3. Да, вы также можете иметь нулевые инициализаторы и конечные условия для цикла for.
  4. Является ли это плохой практикой, зависит от того, как она используется. Используя эту технику, вы можете создать довольно загадочный код, однако это не означает, что при правильном использовании он вполне приемлем.
  5. Я сомневаюсь в этом!
4 голосов
/ 02 июня 2011

В циклах C и Java for вы можете указать неограниченное (или нулевое) число инициализаторов, разделенных запятыми (одного и того же типа данных) и действий в конце цикла.

Раздел инициализатора - это всего лишь один оператор Java; Определения Java-переменных могут быть сгруппированы, если они относятся к одному и тому же типу, например:

int i = 0;
int j = 0;

эквивалентно:

int i = 0, j = 0;

В разделе «конец цикла» вы можете делать то, что вам нравится - любое количество операторов, разделенных запятыми.

Начиная с Java 5, также существует синтаксис foreach, например:

List<String> list;
for (String element : list) {
    // do something with the variable element
}

Синтаксис работает с Iterable типами, включая массивы и коллекции.

1 голос
/ 02 июня 2011

Согласно моим исследованиям и исследованиям, этот тип «условной конструкции» очень часто встречается при обработке изображений, скажем (на самом деле, скажем), вам нужно сравнить пиксели между двумя изображениями в (скажем, снова) противоположных направлениях.Или даже пересечь соседей в противоположных направлениях тоже.Другой пример этого типа «условной конструкции», когда вам нужно преобразовать 32-битный bmp в 24-битный или наоборот.(только то, что приращение будет разностным, как i = i + 4, j = j + 3).

1 голос
/ 02 июня 2011
  1. Java поддерживает это для структуры. В других языках, таких как C, вы можете использовать любое выражение вместо условия условия. Этот пункт не должен быть связан и может использовать разные переменные.

    for({declaration-clause}; {condition-clause}; {expression-clause})
    
  2. Синтаксис является просто расширением декларации и нотации выражений в C.

    int i = 10, j = 0; // a declaration
    j++, i--; // an expression.
    
  3. Слишком много, чтобы упомянуть.

  4. Это может быть плохой практикой, если вы не можете определить, что означает код. В вашем случае вы смогли понять это, даже если бы не написали так.
  5. цикл for никогда не требуется требуется Вы всегда можете заменить его на цикл while, поэтому нет.
0 голосов
/ 14 сентября 2013

В контексте C # (который будет рассматриваться только в моем ответе) нет оператора запятой , как в C ++ и C, поэтому техническая ссылка на оператор запятой некорректна для C #.

Но оператор C # for все еще был предназначен для создания конструкций, которые люди уже знали из C ++.

Точные правила можно найти в разделе Оператор for в спецификации языка C # . Мы видим, что третий и последний «компонент» в операторе for (;;) определяется как список выражений операторов , а список выражений операторов определяется в рекурсивным способом, в виде списка выражений операторов, разделенных символом , (запятая).

Итак, это объясняет j++, i-- часть.

Мы также видим, что первой частью в for (;;) может быть либо объявление локальной переменной , либо список выражений-выражений . Первый также может содержать запятую. Объявление int i = 10, j = 0; допускается как независимое утверждение (т. Е. Не внутри for), а также должно интерпретироваться как объявление локальной переменной .

0 голосов
/ 02 июня 2011

В ответ на 5 одно общее использование выглядит примерно так (в C):

for (size_t pos = 0, end = strlen(something); pos < end; ++pos) {
    if isspace((unsigned)(something[pos])) {
        puts("contains whitespace");
        break;
    }
}

вместо:

for (size_t pos = 0; pos < strlen(something); ++pos) { ... }

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

Конечно, вместо этого вы могли бы написать это:

const size_t end = strlen(something);
for (size_t pos = 0; pos < end; ++pos) { ... }

Так что, безусловно, нет строгой необходимости определять дополнительную переменнуюв утверждении for, и, возможно, есть крошечное преимущество, если в любом случае сделать end const (или final), который вы потеряете, определив pos и end вместе.Некоторые люди предпочитают, чтобы «переменные цикла» (в данном случае включая «константу цикла») определялись в операторе for, поскольку они «принадлежат» ему.

Плюс, до того, как кто-то указываетв моем примере на C вам вообще не нужна индексация:

for (const char *ptr = something; *ptr; ++ptr) { 
    if isspace((unsigned)*ptr) { ... }
}

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

0 голосов
/ 02 июня 2011

вывод: 0 9 8 7 6


  1. Языки на основе почти синтаксиса C реализуют эту функцию.
  2. Откуда этот синтаксис происходит от c.
  3. Одна часть, две части, три части, все может исчезнуть. Не иметь никакой части, цикл может работать правильно.
  4. Это плохая практика.
  5. Я не знаю.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...