Чем отличается цикл по переменной от цикла n раз и от цикла с условием
Перейти к содержимому

Чем отличается цикл по переменной от цикла n раз и от цикла с условием

  • автор:

Цикл

Цикл в программировании — это управляющая конструкция, которая заставляет какой-то блок кода выполняться несколько раз. Циклы есть в большинстве языков программирования. Чаще всего их объявляют командами for, while или repeat.

«IT-специалист с нуля» — наш лучший курс для старта в IT

Блок кода, который нужно повторять, называется телом цикла. Там может быть практически любой код, необходимый разработчику. Сама управляющая конструкция описывает, как долго повторять действия: можно задать четкое количество повторений или заставить цикл выполняться до достижения какого-то условия.

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

условие и тело цикла, схема

Профессия / 8 месяцев
IT-специалист с нуля

Попробуйте 9 профессий за 2 месяца и выберите подходящую вам

vsrat_7 1 (1)

Кто и зачем пользуется циклами

Циклы встречаются в работе большинства программистов практически на всех языках программирования. Они есть в Java, JavaScript, PHP, Python, C++ — везде. Циклов в привычном виде нет разве что в очень низкоуровневых языках, близких к «железу», таких как ассемблер.

Циклами пользуются для задач, в рамках которых нужно повторить одно и то же действие несколько раз. Таких задач в разработке много. Например:

  • построчно посчитать данные из файла;
  • обработать каждый элемент структуры данных по очереди;
  • видоизменить целый ряд данных;
  • решить математическую задачу;
  • обновить экран.
  • решить математическую задачу;
  • обновить экран.

Некоторые алгоритмы итеративные, то есть работают с несколькими итерациями — повторениями. Чтобы их реализовать, обычно нужны циклы.

Частичной альтернативой циклам в программировании можно назвать некоторые встроенные функции высшего порядка. Они могут выполнять несколько действий, например с массивом. Но такие функции есть не во всех языках и подходят не для всего. Циклы универсальнее.

Какими бывают циклы

Обычно в языках программирования несколько видов циклов. У каждого из них свое назначение. Разберемся с основными.

For. Это цикл, который выполняется заданное количество раз. В большинстве C-подобных языков (то есть таких, которые внешне похожи на C и C++) он имеет похожую структуру:

Объявление переменной — это создание итератора, переменной, которая будет изменяться на каждом шаге цикла. Когда итератор достигнет какого-то значения, цикл закончится.

Условие описывает, должен ли цикл продолжаться. Пока оно выполняется, цикл работает. Чаще всего условие — итератор меньше или больше определенного числа. Соответственно, когда итератор достигает указанного значения, цикл останавливается.

Действие — то, что нужно выполнить в конце каждой итерации. Сюда записывают изменения итератора: например, добавить к нему единицу или отнять какое-то число.

принцип работы цикла

Стандартный случай — задается итератор, равный 0. Условие — итератор меньше какого-нибудь N. Действие — прибавить к итератору единицу. Цикл будет выполняться N+1 раз, потому что отсчет идет с нуля. Есть и вариации, их рассмотрим ниже.

В некоторых языках, таких как Python или Pascal, синтаксис цикла For другой. Но суть та же: им пользуются, когда нужное количество действий известно заранее.

Курс для новичков «IT-специалист
с нуля» – разберемся, какая профессия вам подходит, и поможем вам ее освоить

For in / for of / for-each. Частный случай цикла for — когда нужно пройти по структуре данных. Для этого в некоторых языках существует особая конструкция. Это цикл for, который вместо итератора работает с самой структурой данных, например массивом или объектом.

В JavaScript такой цикл называется for of (есть еще for in, но он работает немного иначе), в Java — for-each, в Python — for in. Синтаксис примерно такой:

for in*  < > * in приведено для примера. В JS там не in, а of, а в Java и C++ — вообще двоеточие.

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

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

Не стоит путать этот цикл с функцией forEach(). Она проходит по массиву и что-то делает с каждым его элементом. Но это не цикл, а одна из функций высшего порядка, о которых мы говорили ранее.

While с предусловием. While — совсем другой случай. Это цикл, который используют, если нужное количество действий заранее неизвестно. Он может выполниться несколько раз, один раз, бесконечное количество раз — или вообще не выполниться ни разу. Слово while переводится как «пока» и отражает суть: пока верно какое-то условие, цикл выполняется.

Синтаксис у него примерно такой, хотя в зависимости от языка детали могут различаться:

while () < >

Он выглядит проще, чем for, и иногда используется как его альтернатива. Но суть у этого цикла другая. Например, мы заранее не знаем, сколько шагов придется сделать, чтобы реализовать итерационный алгоритм. В таких случаях и нужен while.

While с постусловием. Это вариация цикла while. Ее единственное отличие — условие пишется не до, а после тела цикла:

do < > while ();

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

Repeat / until. В некоторых языках есть еще один вид цикла — repeat или until. Он похож на while, но, в отличие от него, описывает не условие для продолжения, а условие для выхода из цикла. Цикл закончится, когда условие окажется верным.

В Pascal это цикл с постусловием, который описывается так:

repeat < > until (); А, например, в Perl это цикл с предусловием: until () < >

Этот вид цикла не прижился в разработке и встречается довольно редко. В большинстве языков его нет. Но о нем стоит помнить, если вы хотите писать на языках, где существует такая конструкция.

Как можно модифицировать циклы

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

Цикл по структуре данных. Это цикл for in / for of, о котором мы уже говорили. Обычным циклом тоже можно пройти по массиву: запустить счетчик от 0 до длины структуры данных. Но тут есть детали, которые стоит прояснить подробнее.

Структура данных должна быть итерируемой. Это свойство некоторых сущностей в программировании — оно говорит, что содержимое сущности можно перечислить. У итерируемых объектов есть внутренний итератор — указатель, который служит для перечисления.

Если структура не итерируемая, то пройти по ней циклом for in / for of обычно нельзя. В большинстве языков программирования это работает именно так.

Структуру можно сделать итерируемой: написать свой итератор или присоединить стандартный. Такая возможность есть в большинстве языков программирования и помогает тонко управлять перебором структур данных.

Курс для новичков «IT-специалист
с нуля» – разберемся, какая профессия вам подходит, и поможем вам ее освоить

Цикл в обратную сторону. Тут все проще: цикл for можно запустить не только от 0 до какого-то числа, но и наоборот. Тогда итератор на каждом шаге будет не увеличиваться, а уменьшаться на единицу. Соответственно, цикл пройдет столько же раз, но в обратном направлении. Иногда эта возможность бывает полезной.

Кстати, повернуть цикл for в другую сторону — не единственная его модификация. Можно, например, сделать цикл с шагом 2 и больше, чтобы итератор изменялся не на 1, а на 2 или другое число. Можно вообще не складывать с ним или вычитать из него числа, а умножать или делить. Вариаций много. Но используют их не так часто, чтобы не усложнять код: хорошая практика — делать программу понятной и легко читаемой.

Вложенный цикл. Его иногда называют двойным циклом. Это цикл, в который вложен другой цикл. Чаще всего это цикл for.

Тело внешнего цикла — внутренний цикл. А уже внутри могут быть разные подпрограммы.

схема циклов

Вложенными циклами могут пользоваться, например, для вычислений в двумерных структурах данных: матрицах, тензорах. Но в целом считается, что если использования вложенных циклов можно избежать, лучше так и сделать.

На каждом шаге внешнего цикла выполняется весь внутренний, целиком. Если внешний цикл происходит N раз, а внутренний — M раз, то общее количество итераций во вложенном цикле — N * M. Это много. Поэтому вложенные циклы ресурсоемкие, и ими пользуются, только если без них не обойтись. К тому же конструкция из двух циклов друг в друге серьезно усложняет читаемость кода.

Бесконечный цикл. Если в цикле нет условия для выхода, подпрограмма внутри него будет повторяться бесконечно. Она завершится, только если прервать исполнение всего кода.

Бесконечным чаще всего становится цикл while, но в теории таким можно сделать и цикл for — например, если не менять итератор или установить невозможное условие для выхода.

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

Как прервать цикл

Существует несколько способов остановить выполнение цикла. Чаще всего используют тот, который предусмотрен самой конструкцией, — невыполнение условия. Но есть и другие возможности. Пользоваться ими не всегда корректно.

Невыполнение условия. Наиболее частый вариант: условие для продолжения цикла больше не выполняется. Итератор достиг нужного значения, или переменная, которую нужно было отыскать, найдена. Тогда цикл прекращается, программа начинает выполнять код, который был написан после него.

Условия могут быть разными, в том числе сложными, состоящими из нескольких частей. Ниже мы расскажем про break и continue — в большинстве случаев грамотно составленные условия помогают обойтись без использования этих операторов.

Пропуск итерации. Если по какой-то причине в цикле нужно закончить итерацию раньше времени и перейти на следующую, для этого есть специальная команда. Обычно она называется continue. По сути, это оператор, который говорит программе: «Закончи это повторение, продолжи со следующего шага».

Continue нужно просто написать в том месте, где вы хотите выйти из текущей итерации, — например, если в программе выполнилось какое-то условие.

На практике continue используется не слишком часто, во многих языках его считают плохой практикой. Его можно заменить условным оператором.

Выход из цикла. Можно экстренно выйти из цикла в любой момент. Для этого во многих языках программирования существует команда break. Она означает «Прерви выполнение подпрограммы и выйди из нее».

Когда программа доходит до этой команды, она выходит из цикла или условия и начинает выполнять код, который идет дальше. Никаких итераций больше не происходит. Например, мы искали в цикле какое-то число, нашли его — дальше проходить по циклу не нужно. Можно из него выйти.

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

Команду можно использовать не только в циклах, но и, например, в условных структурах if/else.

Пауза. Некоторые языки дают возможность временно приостановить цикл, как бы установить задержку для его выполнения. Например, подождать несколько секунд — для этого есть специальные функции. Как реализовать такую возможность, зависит от языка и ситуации.

Что нужно знать при использовании циклов

В некоторых языках циклы использовать принято, только если нет альтернативы. Это связано с особенностями подхода к программированию. Например, в JavaScript считается хорошей практикой по возможности пользоваться для обработки массивов функциями высшего порядка, а к циклам прибегать, только если иначе никак.

Циклы, особенно вложенные, отнимают довольно много ресурсов. Они менее ресурсоемкие, чем рекурсия, но, если неоптимально решить какую-то задачу, можно потерять много памяти. Это стоит учитывать при реализации алгоритмов.

Пользоваться операторами break и continue без нужды — плохая практика в большинстве языков программирования. Они нарушают структуру программы, делают код менее логичным и предсказуемым.

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

Хотите узнать больше про языки программирования? Запишитесь на курсы и станьте разработчиком на одном из востребованных современных языков.

IT-специалист с нуля

Наш лучший курс для старта в IT. За 2 месяца вы пробуете себя в девяти разных профессиях: мобильной и веб-разработке, тестировании, аналитике и даже Data Science — выберите подходящую и сразу освойте ее.

Циклы

П ри решении практических задач постоянно возникает необходимость в повторении действия заданное количество раз, или до достижения какого-либо условия. Например, вывести список всех пользователей, замостить плоскость текстурой, провести вычисления над каждым элементом массива данных и т.п. В си для этих целей используются три вида циклов: с предусловием, постусловием и цикл for со счётчиком (хотя, это условное название, потому что счётчика может и не быть).

Любой цикл состоит из тела и проверки условия, при котором этот цикл должен быть прекращён. Тело цикла — это тот набор инструкций, который необходимо повторять. Каждое повторение цикла называют итерацией.

Рассмотрим цикл с предусловием.

int i = 0; while (i

Этот цикл выполняется до тех пор, пока истинно условие, заданное после ключевого слова while. Тело цикла — это две строки, одна выводит число, вторая изменяет его. Очевидно, что этот цикл будет выполнен 10 раз и выведет на экран
0
1 2 3 и так далее до 9.

Очень важно, чтобы условие выхода из цикла когда-нибудь выполнилось, иначе произойдёт зацикливание, и программа не завершится. К примеру

int i = 0; while (i

В этом цикле не изменяется переменная i, которая служит для определения условия останова, поэтому цикл не завершится.

int i = 0; while (i > 0)

В этой программе цикл, конечно, завершится, но из-за неправильного действия он будет выполнен гораздо больше 10 раз. Так как си не следит за переполнением переменной, нужно будет ждать, пока переменная переполнится и станет меньше нуля.

int i; while (i

У этого примера неопределённое поведение. Так как переменная i заранее не инициализирована, то она хранит мусор, заранее неизвестное значение. При различном содержимом переменной i будет меняться поведение.

Если тело цикла while содержит один оператор, то фигурные скобки можно опустить.

int i = 0; while (i < 10) printf("%d\n", i++);

Здесь мы инкрементируем переменную i при вызове функции printf. Следует избегать такого стиля кодирования. Отсутствие фигурных скобок, особенно в начале обучения, может приводить к ошибкам. Кроме того, код читается хуже, да и лишние скобки не сильно раздувают листинги.

Циклы с постусловием.

Ц икл с постусловием отличается от цикла while тем, что условие в нём проверяется после выполнения цикла, то есть этот цикл будет повторён как минимум один раз (в отличие от цикла while, который может вообще не выполняться). Синтаксис цикла

do < тело цикла >while(условие);

Предыдущий пример с использованием цикла do будет выглядеть как

int i = 0; do < printf("%d\n", i); i++; >while(i < 10);

Давайте рассмотрим пример использования цикла с постусловием и предусловием. Пусть нам необходимо проинтегрировать функцию.

Численное интегрирование функции

∫ a b f ⁡ x d x

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

∫ a b f ⁡ x d x = ∑ i = a b f ⁡ i h

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

Пусть искомой функцией будет x 2 . Нам понадобятся следующие переменные. Во-первых, аккумулятор sum для хранения интеграла. Во-вторых, левая и правая границы a и b, в третьих - шаг h. Также нам понадобится текущее значение аргумента функции x.

Для нахождения интеграла необходимо пройти от a до b с некоторым шагом h, и прибавлять к сумме площадь прямоугольника со сторонами f(x) и h.

#include #include int main() < double sum = 0.0; double a = 0.0; double b = 1.0; double h = 0.01; double x = a; while (x < b) < sum += x*x * h; x += h; >printf("%.3f", sum); getch(); >

Программа выводит 0.328.

∫ 0 1 x 2 d x = x 3 3 | 0 1 = 1 3 ≈ 0.333

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

while (x

Сумма в этом случае будет равна 0.338. Метод левых и правых прямоугольников не очень точен. Мы фактически аппроксимировали (приблизили) гладкий график монотонно возрастающей функции гистограммой. Если немного подумать, то аппроксимацию можно проводить не только суммируя прямоугольники, но и суммируя трапеции.

Приближение с помощью трапеций на самом деле является кусочной аппроксимацией кривыми первого порядка (ax+b). Мы соединяем точки на графике с помощью отрезков. Можно усложнить, соединяя точки не отрезками, а кусками параболы, тогда это будет метод Симпсона. Если ещё усложнить, то придём к сплайн интерполяции, но это уже другой, очень долгий разговор.

Вернёмся к нашим баранам. Рассмотрим 4 цикла.

int i = 0; while ( i++
int i = 0; while ( ++i

int i = 0; do < printf("%d ", i); >while(i++ < 3);
int i = 0; do < printf("%d ", i); >while(++i < 3);

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

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

Давайте решим простую задачу. Пользователь вводит числа до тех пор, пока не будет введено число 0, после этого выводит самое большое из введённых. Здесь есть одна загвоздка. Сколько чисел введёт пользователь не известно. Поэтому мы создадим бесконечный цикл, а выходить из него будем с помощью оператора break. Внутри цикла мы будем получать от пользователя данные и выбирать максимальное число.

#include #include int main() < int num = 0; int max = num; printf("To quit, enter 0\n"); /*бесконечный цикл*/ while (1) < printf("Please, enter number: "); scanf("%d", &num); /*условие выхода из цикла*/ if (num == 0) < break; >if (num > max) < max = num; >> printf("max number was %d", max); getch(); >

Напомню, что в си нет специального булевого типа. Вместо него используются числа. Ноль - это ложь, все остальные значения – это истина. Цикл while(1) будет выполняться бесконечно. Единственной точкой выхода из него является условие

if (num == 0)

В этом случае мы выходим из цикла с помощью break; Для начала в качестве максимального задаём 0. Пользователь вводит число, после чего мы проверяем, ноль это или нет. Если это не ноль, то сравниваем его с текущим максимальным.

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

Когда нам необходимо пропустить тело цикла, но при этом продолжить выполнение цикла, используется оператор continue. Простой пример: пользователь вводит десять чисел. Найти сумму всех положительных чисел, которые он ввёл.

#include #include int main() < int i = 0; int positiveCnt = 0; float sum = 0.0f; float input; printf("Enter 10 numbers\n"); while (i < 10) < i++; printf("%2d: ", i); scanf("%f", &input); if (input sum += input; positiveCnt++; > printf("Sum of %d positive numbers = %f", positiveCnt, sum); getch(); >

Пример кажется несколько притянутым за уши, хотя в общем он отражает смысл оператора continue. В этом примере переменная positiveCnt является счётчиком положительных чисел, sum сумма, а input - временная переменная для ввода чисел.

Вот ещё один пример. Необходимо, чтобы пользователь ввёл целое число больше нуля и меньше 100. Пока необходимое число не будет введено, программа будет продолжать опрос.

do < printf("Please, enter number: "); scanf("%d", &n); if (n < 0 || n>100) < printf("bad number, try again\n"); continue; >else < break; >> while (1);

Цикл for

О дним из самых используемых является цикл со счётчиком for. Его синтаксис

Например, выведем квадраты первых ста чисел.

int i; for (i = 1; i

Одним из замечательных моментов цикла for является то, что он может работать не только с целыми числами.

float num; for (num = 5.3f; num > 0f; num -= 0.2)

Этот цикл выведет числа от 5.3 до 0.1. Цикл for может не иметь некоторых "блоков" кода, например, может отсутствовать инициализация, проверка (тогда цикл становится бесконечным) или изменение счётчика. Вот пример с интегралом, реализованный с применением счётчика for

#include #include int main() < double sum = 0.0; double a = 0.0; double b = 1.0; double h = 0.01; double x; for (x = a; x < b; x += h) < sum += x*x * h; >printf("%.3f", sum); getch(); >

Давайте рассмотрим кусок кода

double x ; for (x = a; x

Его можно изменить так

double x = a; for (; x

Более того, используя оператор break, можно убрать условие и написать

double x; for (x = a;; x += h)< if (x>b) < break; >sum += x*x*h; >
double x = a; for (;;) < if (x >b) < break; >sum += x*x*h; x += h; >

кроме того, используя оператор ",", можно часть действий перенести

double x ; for (x = a; x < b; x += h, sum += x*x*h) ;

ЗАМЕЧАНИЕ: несмотря на то, что так можно делать, пожалуйста, не делайте так! Это ухудшает читаемость кода и приводит к трудноуловимым ошибкам.

Давайте решим какую-нибудь практическую задачу посложнее. Пусть у нас имеется функция f(x). Найдём максимум её производной на отрезке. Как найти производную функции численно? Очевидно, по определению). Производная функции в точке - это тангенс угла наклона касательной.

Численное дифференцирование функции

f ⁡ x ′ = d x d y

Возьмём точку на кривой с координатами (x; f(x)), сдвинемся на шаг h вперёд, получим точку (x+h, f(x+h)), тогда производная будет

d x d y = f ⁡ ( x + h ) - f ⁡ x ( x + h - x ) = tg ⁡ α

То есть, отношение малого приращения функции к малому приращению аргумента. Внимательный читатель может задать вопрос, почему мы двигаемся вперёд по функции, а не назад. Ну пойдёмте назад

Возьмём среднее от этих двух значений, получим

В общем-то теперь задача становится тривиальной: идём от точки a до точки b и находим минимальное значение производной, а также точку, в которой производная принимает это значение. Для решения нам понадобятся, как и в задаче с интегралом, переменные для границ области поиска a и b, текущее значение x и шаг h. Кроме того, необходимо максимальное значение maxVal и координата maxX этого максимального значения. Для работы возьмём функцию x • sin ⁡ x

#include #include #include int main() < double a = 0; double b = 3.0; double h = 0.001; double h2 = h * 2.0; double maxVal = a*sin(a); double maxX = a; double curVal; double x; // Проходим по всей области от a до b // и ищем максимум первой производной // Используем функцию x*sin(x) for (x = a; x < b; x += h) < curVal = ( (x+h)*sin(x+h)-(x-h)*sin(x-h) )/h2; if (curVal >maxVal) < maxVal = curVal; maxX = x; >> printf("max value = %.3f at %.3f", maxVal, maxX); getch(); >

На выходе программа выдаёт max value = 1.391 at 1.077

График производной функции x*sin(x)

Численное решение даёт такие же (с точностью до погрешности) результаты, что и наша программа.

Вложенные циклы

Рассмотрим пример, где циклы вложены друг в друга. Выведем таблицу умножения.

#include #include #include int main() < int i, j; // Для каждого i for (i = 1; i < 11; i++) < // Выводим строку из произведения i на j for (j = 1; j < 11; j++) < printf("%4d", i*j); >// После чего переходим на новую строку printf("\n"); > getch(); >

В этом примере в первый цикл по переменной i вложен второй цикл по переменной j. Последовательность действий такая: сначала мы входим в цикл по i, после этого для текущего i 10 раз подряд осуществляется вывод чисел. После этого необходимо перейти на новую строку. Теперь давайте выведем только элементы под главной диагональю

for (i = 1; i < 11; i++) < for (j = 1; j < 11; j++) < if (j >i) < break; >printf("%4d", i*j); > printf("\n"); >

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

for (i = 1; i < 11; i++) < for (j = 1; j printf("\n"); >

В данном случае мы используем во вложенном цикле счётчик первого цикла.

ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

email

Всё ещё не понятно? – пиши вопросы на ящик

Циклы в языке C

Прежде, чем изучать циклы, следует познакомиться с часто используемым в языке C способом увеличения/уменьшения значений переменных на единицу. Конечно, в C работают такие формы изменения значений как, например, a += 1 или a -= 1. Однако чаще используют операции инкрементирования (оператор инкремента "++") и декрементирования (оператор декремента "--"): i++ или ++i, i-- или --i. В результате этих операций переменные увеличиваются или уменьшаются на единицу.

Запомните, когда вы видите выражения типа ++i или i++, то в результате их выполнения значение i меняется. Не надо делать вот так: i = ++i. Это совершенно лишнее.

Когда знак инкремента или декремента стоит перед переменной, то перед нами префиксная форма операции (++i, --i), а когда после переменной, то постфиксная форма (i++, i--). Когда эти выражения не участвуют в построении более сложных выражений, то между префиксной и постфиксной формами никакой разницы нет: что i++, что ++i — без разницы, в результате мы получим значение i на единицу больше.

Однако когда эти выражения участвуют в построении более сложных, то разница между префиксной и постфиксной формами появляется и заключается в следующем: переменная над которой производится операция инкрементирования или декрементирования в постфиксной форме сначала используется в сложном выражении как есть, и только потом увеличивается на единицу; если мы имеем дело с префиксной формой, то переменная сначала изменяется, а затем используется. Например, код:

int a, b, c, d; /* выражение означает, что всем переменным присваивается 0 */ a = b = c = d = 0; printf("a=%d, b=%d, c=%d, d=%d\n", a, b, c, d); c = ++a; d = b++; printf("a=%d, b=%d, c=%d, d=%d\n", a, b, c, d);

, выведет на экране:

a=0, b=0, c=0, d=0 a=1, b=1, c=1, d=0

Объясняется такой результат так:

  • значение переменной a было увеличено на единицу, после чего это значение было присвоено переменной c;
  • значение переменной b было сначала присвоено переменной d и только потом увеличено на единицу.

Еще один пример:

int x, y; x = y = 0; printf("%d\n", x++ > 0); printf("%d\n", ++y > 0);

На экране будет выведено:

Это результат логических выражений, где 0 означает ложь, а 1 — истину. В данном случае, когда x сравнивается с нулем, то его значение еще не увеличено, а когда сравнивается у, то его значение уже больше нуля.

Применять операторы инкремента и декремента можно также к переменным вещественного типа.

Цикл while

Цикл while в языке программирования C работает также как и в других языках программирования. По аналогии с условным выражением в инструкции if, условное выражение при while заключается в круглые скобки. Если тело цикла включает несколько выражений разделяемых точкой с запятой, то все тело заключается в фигурные скобки.

  1. Присвойте переменной star значение 0. Пока значение star не достигнет 55 выводите на экран в строку по одной звездочке (*).
  2. С помощью цикла while запрограммируйте вывод на экран цифровых кодов и значений таблицы символов ASCII от 31 до 127 включительно. При этом после каждого десятого символа осуществляйте переход на новую строку. (Подсказка: чтобы переходить на новую строку, в цикле while надо использовать инструкцию if , в условии которой остаток 1 от деления на 10 сравнивается с нулем.) Вывод таблицы ASCII с помощью цикла whileВывод таблицы ASCII с помощью цикла while
  3. Используя внешний и вложенный циклы while организуйте вывод таблицы умножения на экран. Вывод таблицы умножения с помощью цикла while

1 Операция нахождения остатка от деления в языке C обозначается знаком процента (%).

Цикл do-while

Цикл do-while отличается от while лишь тем, что его тело будет выполнено хотя бы один раз независимо от условия выполнения цикла. Синтаксис цикла do-while можно описать так (фигурные скобки можно опустить, если выражение только одно):

do < выражение1; …; >while (логич_выражение);

Этот цикл называют циклом с постусловием. Его используют намного реже обычного while. В принципе почти всегда можно обойтись без него, но в определенных ситуациях его использование упрощает код. Допустим требуется вывести на экран отдельные цифры числа. Любое число состоит хотя бы из одной цифры, даже число 0. Можно решить эту задачу с использованием цикла while:

while (a > 0) { printf("%d\n", a % 10); a = a / 10; }

Но в этом случае, если a равно 0, то цикл не выполнится ни разу. Пришлось бы перед циклом использовать инструкцию if, в которой сравнивать переменную с 0. Использование же цикла do-while решает эту проблему, т. к. его тело один раз выполнится даже при нулевом значении переменной:

do { printf("%d\n", a % 10); a = a / 10; } while (a > 0);

Цикл for

Представим синтаксис заголовка цикла for языка программирования C так:

for (часть1; часть2; часть3)

Заголовок цикла for включает три части, разделенных точкой с запятой; причем каждая часть может быть сложной, т. е. состоять из нескольких выражений, разделенных простой запятой. В первой части обычно указываются переменные и часто их начальные значения; во второй – с помощью логического(их) выражения(й) задаются условия, при которых выполняется тело цикла; в третью часть помещаются выражения, которые выполняются в конце каждой итерации цикла (чаще всего здесь изменяется значение переменной, заданной в первой части заголовка).

Вот так будет выглядеть программный код, выводящий таблицу символов на экран, в котором используется цикл for:

unsigned char a; for (a = 31; a  128; a++) { if (a % 10 == 0) printf("\n"); printf("%4d-%c", a, a); } printf("\n");

Напишите программу с использованием цикла for, выводящую на экран таблицу умножения (Подсказка: как и в случае с while следует использовать два цикла — внешний и вложенный.)

Операторы break и continue

Оператор break позволяет прервать выполнение цикла, а continue — прервать текущую итерацию (проход) цикла. Почти всегда можно обойтись без этих операторов, но иногда их использование позволяет упростить программный код и сделать его более понятным.

Рассмотрим пару примеров. Допустим, требуется проверить массив на наличие в нем хотя бы одного элемента со значением 0. Как только ноль будет обнаружен проверять оставшуюся часть массива уже нет смысла. Поэтому, чтобы не выполнять лишних итераций, используется оператор break.

#define N 10 int arr[N] = {6, 5, -4, 3, -7, 2, 7, 0, 3, 9}; int new_arr[N], i, j; for (i=0; iN; i++) { if (arr[i] == 0) { printf("\nIt contains zero"); break; } // не обязательно printf("%d ", arr[i]); } printf("\n");

Второй пример. Требуется из одного массива скопировать в другой только числа, которые больше 0. Можно с помощью continue прерывать итерацию цикла, если очередной элемент меньше либо равен нулю.

for(i=0, j=0; iN; i++) { if (arr[i]  0) continue; new_arr[j] = arr[i]; printf("%d ", new_arr[j]); j++; } printf("\n");

В данном случае использование continue совсем не очевидно с точки зрения надобности, т. к. легко можно обойтись без него, если изменить условие при if на противоположное, удалить continue, а оставшийся код поместить в тело оператора if.

Курс с решением части задач:
pdf-версия

Цикл for и изменение переменных — Введение в программирование

Этот урок будет быстрым и простым, так что пристегнитесь.

Вызовем функцию факториала с циклом while:

const factorial = (n) =>  let counter = 1; let result = 1; while (counter  n)  result = result * counter; counter = counter + 1; > return result; > 

Когда мы работаем с переменными, мы часто поступаем так: меняем их значения, прибавляя к ним сколько-нибудь или умножая их на что-то. Или просто прибавляем или вычитаем единицу.

Как и во многих других языках программирования в JavaScript есть для этого упрощенные формы.

Вместо result = result * counter вы можете сказать result *= counter . Результат будет тот же самый, это просто способ сократить запись. Точно так же вы можете поступить со знаками плюс, минус и остатком от деления:

Добавление единицы к переменной — тоже очень типичная операция, поэтому вместо counter = counter + 1 можно записать counter++ . Так же для минуса — counter = counter - 1 равносильно counter-- . Это операторы инкрементирования и декрементирования.

Есть два вида, проще их понять на примере:

// Postfix let a = 3; let b; b = a++; // b = 3, a = 4 // Prefix let a = 3; let b; b = ++a; // b = 4, a = 4 

Если вы поставите ++ после имени переменной — это постфиксная нотация — то фактическое сложение произойдёт после того, как значение вернётся. Вот почему b тут 3: оно получает значение перед тем как меняется a .

Если вы поставите ++ перед именем переменной — это префиксная нотация — то фактическое сложение произойдёт перед тем, как значение вернётся. Вот почему b тут 4: оно получает значение после того как меняется a .

Но в конце в обоих случаях a становится 4.

Это обновлённая функция факториала:

const factorial = (n) =>  let counter = 1; let result = 1; while (counter  n)  result *= counter; counter++; > return result; > 

Здесь не имеет значения, используем мы префикс или постфикс когда инкрементируем counter , потому что значение не хранится больше нигде.

Этот код немного проще и короче. Иметь цикл — этот повторяющийся код — со счётчиком контролирующим повторения — распространённый в программировании приём. Поэтому кроме цикла while существует цикл for. В нем есть встроенные счетчики.

Это та же функция факториала, но с циклом for вместо цикла while:

const factorial = (n) =>  let result = 1; for (let counter = 1; counter  n; counter++)  result *= counter; > return result; > 

Здесь три момента:

  1. Инициализация счётчика.
  2. Условие цикла. Так же как и в цикле while, этот цикл будет повторяться пока это условие истинно.
  3. Обновление счётчика. Как менять счётчик в каждом шаге.

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

Пришло время использовать все эти навороченные знания и написать код! Переходите к тесту и упражнению прямо сейчас!

Дополнение к уроку

Скрытые сложности

Операции декремента и инкремента кажутся мощными механизмами, но их использование привносит ненужную сложность в программы. Код, написанный с их использованием, часто превращается в ребус. Попробуйте ответить, чему равно значение:

let x = 5; let y = 10; console.log(x++ + ++y); 

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

Во многих языках таких операций нет в принципе. Линтеры (программы, проверяющие код на соответствие стандартам) в JS настроены так, чтобы "ругаться" при виде этих операций в коде. Вместо них предлагается делать так:

x += 1; // x = x + 1; 

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

Соответствующее правило в eslint: https://eslint.org/docs/rules/no-plusplus

Switch

Конструкция switch может заменить собой несколько условий if . Вот пример обычного условия с if :

let answer; if (num === 1)  answer = "One"; > else if (num === 2)  answer = "Two"; > else  answer = "Nothing"; > 

А вот как его можно переписать с помощью switch :

switch(num)  case 1: // if (num === 1) answer = "One"; break; case 2: // if (num === 2) answer = "Two"; break; default: answer = "Nothing"; break; > 

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

Если в примере выше убрать все break 'и, а num будет равен 1, то выполнятся все строки:

answer = "One"; answer = "Two"; answer = "Nothing"; 

Так что в итоге answer будет иметь значение "Nothing".

Несколько значений case можно группировать.

switch(num)  case 1: // if (num === 1) answer = "One"; break; case 2: // if (num === 2) case 3: // if (num === 3) case 4: // if (num === 4) answer = "Two to four"; break; default: answer = "Nothing"; break; > 

Выводы

Операторы инкрементирования и декрементирования:

// Postfix let a = 3; let b; b = a++; // b = 3, a = 4 // Prefix let a = 3; let b; b = ++a; // b = 4, a = 4 
const factorial = (n) =>  let result = 1; // initialization↓ condition↓ update↓ for (let counter = 1; counter  n; counter++)  result *= counter; > return result; > 

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

Например, в блоке инициализации не требуется определять переменные:

let counter = 1; for (; counter  n; counter++)  // любой код > 

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

for (let counter = 1;; counter++)  if (counter  n) break; // любой код > 

Вы можете пропустить все 3 блока. Снова убедитесь, что используете break, чтоб закончить цикл, а также изменить счётчик так, чтоб условие для break было истинно в нужный момент.

let counter = 1; for (;;)  if (counter >= n) break; // любой код counter++; > 

Обратите внимание на то, что если внутри тела цикла использовать оператор return , то выполнение цикла будет прервано и функция вернет значение.

// Функция должна посчитать сумму всех чисел от 1 до n const sum = (n) =>  let result = 0; for (let counter = 1; counter  n; counter++)  return 10; // return прерывает цикл result += counter; > return result; > sum(5); // 10 sum(20); // 10 sum(50); // 10 

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Об обучении на Хекслете

  • Статья «Как учиться и справляться с негативными мыслями»
  • Статья «Ловушки обучения»
  • Статья «Сложные простые задачи по программированию»
  • Урок «Как эффективно учиться на Хекслете»
  • Вебинар « Как самостоятельно учиться »

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *