Цикл із передумовою - це
Послідовність інструкцій, призначена для багаторазового виконання, називається тілом циклу. Одноразове виконання тіла циклу називаєтьсяітерацією. Вираз визначальний, буде в черговий раз виконуватися ітерація, або цикл завершиться, називаєтьсяумовою виходуабоумовою закінчення циклу(абоумовою продовженнязалежно від того, як інтерпретується його істинність - як ознака необхідності завершення чи продовження циклу). Змінна, що зберігає поточний номер ітерації, називаєтьсялічильником ітераційциклу або простолічильником циклу. Цикл необов'язково містить лічильник, лічильник ні бути один — умова виходу з циклу може залежати від кількох змінних у циклі змінних, а може визначатися зовнішніми умовами (наприклад, настанням певного часу), у разі лічильник може взагалі знадобитися.
Виконання будь-якого циклу включає початкову ініціалізацію змінних циклу, перевірку умови виходу, виконання тіла циклу та оновлення змінної циклу на кожній ітерації. Крім того, більшість мов програмування надають кошти для дострокового завершення циклу, тобто виходу з циклу незалежно від істинності умови виходу.
Види циклів
Безумовні цикли
Іноді у програмах використовуються цикли, вихід із яких не передбачено логікою програми. Такі цикли називаються безумовними, чи нескінченними. Спеціальних синтаксичних засобів для створення нескінченних циклів, зважаючи на їх нетиповість, мови програмування не передбачають, тому такі цикли створюються за допомогою конструкцій, призначених для створення звичайних (абоумовних) циклів. Для забезпечення нескінченного повторення перевірка умови у такому циклі або відсутня(якщо дозволяє синтаксис, як, наприклад, у цикліLOOP…END LOOPмови Ада), або замінюється константним значенням (while true do …у Паскалі).
Цикл із передумовою
Цикл з передумовою - цикл, який виконується поки що істинно деяка умова, вказана перед його початком. Ця умова перевіряється до виконання тіла циклу, тому тіло може бути не виконано жодного разу (якщо умова з самого початку хибна). У більшості процедурних мов програмування реалізується операторомwhile, звідси його друга назва - while-цикл.
Цикл із постумовою
Цикл з постумовою - цикл, в якому умова перевіряється після виконання тіла циклу. Звідси випливає, що тіло завжди виконується хоча б один раз. У Паскаль цей цикл реалізує оператор repeat..until; в Сі - do ... while.
У трактуванні умови циклу з умовою в різних мовах є відмінності. У Паскалі та мовах, що походять від нього, умова такого циклу трактується якумова виходу(цикл завершується, коли умова істинна, в українській термінології такі цикли називають ще «цикл до»), а в Сі та його нащадках — якумова продовження(цикл завершується, коли умова помилкова, такі цикли іноді називають «цикл поки»)…..
Цикл із виходом із середини
Цикл із виходом із середини — найбільш загальна форма умовного циклу. Синтаксично такий цикл оформляється за допомогою трьох конструкцій: початку циклу, кінця циклу та команди виходу з циклу. Конструкція початку маркує точку програми, де починається тіло циклу, конструкція кінця — точку, де тіло закінчується. Всередині тіла має бути присутня команда виходу з циклу, при виконанні якої цикл закінчується і управління передається на оператор, що йде за конструкцією кінця циклу.Природно, щоб цикл виконався більше одного разу, команда виходу має викликатися не безумовно, лише при виконанні умови виходу з циклу.
Принциповою відмінністю такого виду циклу від розглянутих вище є те, що частина тіла циклу, розташована після початку циклу і до команди виходу, виконується завжди (навіть якщо умова виходу з циклу істинно за першої ітерації), а частина тіла циклу, що знаходиться після команди виходу, не виконується за останньої ітерації.
Легко бачити, що за допомогою циклу з виходом із середини можна легко змоделювати і цикл із передумовою (розмістивши команду виходу на початку тіла циклу), та цикл із постумовою (розмістивши команду виходу в кінці тіла циклу).
Частина мов програмування містять спеціальні конструкції для організації циклу з виходом із середини. Так, у мові Ада для цього використовується конструкціяLOOP…END LOOPта команда виходуEXITабоEXIT WHEN:
Тут усередині циклу може бути будь-яка кількість команд виходу обох типів. Самі команди виходу принципово не відрізняються, зазвичайEXIT WHENзастосовують, коли перевіряється лише умова виходу, а простоEXIT— коли вихід із циклу виробляється у одному з варіантів складного умовного оператора.
У тих мовах, де подібних конструкцій не передбачено, цикл із виходом із середини може бути змодельований за допомогою будь-якого умовного циклу та оператора дострокового виходу з циклу (такого, якbreakу Сі), або оператора безумовного переходуgoto.
Цикл з лічильником
Цикл із лічильником — цикл, у якому деяка змінна змінює своє значення від заданого початкового значення до кінцевого значення з деяким кроком, і кожного значення цієї змінної тіло циклу виконується один раз. Убільшості процедурних мов програмування реалізується операторомfor, у якому вказується лічильник (так звана «змінна циклу»), необхідну кількість проходів (або граничне значення лічильника) і, можливо, крок, з яким змінюється лічильник. Наприклад, у мові Оберон-2 такий цикл має вигляд:
(тут v - лічильник, b - початкове значення лічильника, e - граничне значення лічильника, s - крок).
Неоднозначне питання значення змінної по завершенні циклу, у якому ця змінна використовувалася як лічильник. Наприклад, якщо у програмі мовою Паскаль зустрінеться конструкція виду:
Виникає питання: яке значення буде в результаті присвоєно змінної 2: 9, 10, 100, можливо, якесь інше? А якщо цикл закінчиться достроково? Відповіді залежать від того, чи збільшується значення лічильника після останньої ітерації і чи змінює транслятор це значення додатково. Ще одне питання: що буде, якщо всередині циклу лічильнику буде явно надано нове значення? Різні мови програмування вирішують ці питання по-різному. У деяких поведінка лічильника чітко регламентована. В інших, наприклад, у тому ж Паскалі, стандарт мови не визначає ні кінцевого значення лічильника, ні наслідків його явної зміни в циклі, але не рекомендує змінювати лічильник явно і використовувати його після завершення циклу без повторної ініціалізації. Програма на Паскалі, яка ігнорує цю рекомендацію, може давати різні результати при виконанні різних систем і використанні різних трансляторів.
Радикально вирішено питання у мові Ада: лічильник вважається описаним у заголовку циклу, і його просто немає. Навіть якщо ім'я лічильника в програмі вже використовується, всередині циклу як лічильник використовується окрема змінна. Лічильнику заборонено явнонадавати будь-які значення, він може змінюватися тільки внутрішнім механізмом оператора циклу. В результаті конструкція
зовні аналогічна вищенаведеному циклу на Паскалі, трактується однозначно: змінноїkбуде присвоєно значення 100, оскільки зміннаi, яка використовується поза цим циклом, не має жодного відношення до лічильникаi, що створюється та змінюється всередині циклу. Вважається, що подібне відокремлення лічильника є найбільш зручним і безпечним: не потрібен окремий опис для нього і мінімальна ймовірність випадкових помилок, пов'язаних із випадковим руйнуванням зовнішніх по відношенню до циклу змінних. Якщо програмісту потрібно включити в готовий код цикл із лічильником, то він може не перевіряти, чи існує змінна з ім'ям, яке він вибрав як лічильник, не додавати опис нового лічильника в заголовок відповідної процедури, не намагатися використовувати один з наявних, але в даний момент «вільних» лічильників. Він просто пише цикл зі змінною-лічильником, ім'я якої йому зручне, і може бути впевнений, що жодної колізії імен не станеться.
Цикл із лічильником завжди можна записати як умовний цикл, перед початком якого лічильнику надається початкове значення, а умовою виходу є досягнення лічильником кінцевого значення; до тіла циклу у своїй додається оператор зміни лічильника на заданий крок. Однак спеціальні оператори циклу зі лічильником можуть ефективніше транслюватися, оскільки формалізований вид такого циклу дозволяє використовувати спеціальні процесорні команди організації циклів.
У деяких мовах, наприклад, Сі та інших, що походять від нього, циклfor, незважаючи на синтаксичну форму циклу зі лічильником, насправді є циклом з передумовою. Тобто вСі конструкція циклу:
фактично є іншою формою запису конструкції:
Тобто в конструкціїforспочатку пишеться довільна пропозиція ініціалізації циклу, потім — умова продовження і, нарешті, деяка операція, що виконується після кожного тіла циклу (це не обов'язково має бути зміна лічильника; це може бути правка покажчика або яка- або зовсім стороння операція). Для мов такого виду вищеописана проблема вирішується дуже просто: змінна-лічильник поводиться абсолютно передбачувано і по завершенні циклу зберігає своє останнє значення.
Вкладені цикли
Існує можливість організувати цикл усередині тіла іншого циклу. Такий цикл називатиметьсявкладеним циклом. Вкладений цикл по відношенню до циклу в тіло якого він вкладений буде іменуватисявнутрішнім циклом, і навпаки цикл в тілі якого існує вкладений цикл буде іменуватисязовнішнімпо відношенню до вкладеного. Усередині вкладеного циклу у свою чергу може бути вкладений ще один цикл, утворюючи наступний рівень вкладеності і так далі. Кількість рівнів вкладеності зазвичай не обмежується.
Повна кількість виконань тіла внутрішнього циклу вбирається у твори числа ітерацій внутрішнього і всіх зовнішніх циклів. Наприклад, взявши три вкладених один в одного циклу, кожен по 10 ітерацій, отримаємо 10 виконань тіла для зовнішнього циклу, 100 для циклу другого рівня і 1000 у самому внутрішньому циклі.
Однією з проблем, пов'язаних із вкладеними циклами, є організація дострокового виходу з них. У багатьох мовах програмування є оператор дострокового завершення циклу (breakу Сі,exitу Турбо Паскалі,lastу Perl тощо), але він, як правило, забезпечує вихід тільки з циклу того рівня,звідки викликано. Виклик його з вкладеного циклу призведе до завершення тільки цього внутрішнього циклу, а об'ємний цикл продовжить виконуватися. Проблема може здатися надуманою, але вона дійсно іноді виникає при програмуванні складної обробки даних, коли алгоритм вимагає негайного переривання у певних умовах, наявність яких можна перевірити лише у глибоко вкладеному циклі.
Вирішень проблеми виходу із вкладених циклів кілька.
- Найпростіший - використовувати оператор безумовного переходуgotoдля виходу в точку програми, що безпосередньо слідує за вкладеним циклом. Цей варіант критикується прихильниками структурного програмування, як і всі конструкції, що вимагають використання goto. Деякі мови програмування, наприклад Modula-2, просто не мають оператора безперечного переходу, і в них подібна конструкція неможлива.
- Альтернатива — використовувати штатні засоби завершення циклів, у разі потреби встановлюючи спеціальні прапори, які потребують негайного завершення обробки. Недолік — ускладнення коду, зниження продуктивності без будь-яких переваг, крім теоретичної «правильності» через відмову від goto.
- Розміщення вкладеного циклу у процедурі. Ідея полягає в тому, щоб всю дію, яка може знадобитися перервати достроково, оформити у вигляді окремої процедури, і для дострокового завершення використовувати оператор виходу з процедури (якщо така є в мові програмування). У Сі, наприклад, можна побудувати функцію з вкладеним циклом, а вихід з неї організувати за допомогою оператораreturn. Недолік - виділення фрагмента коду в процедуру не завжди логічно обґрунтовано, і не всі мови мають штатні засоби дострокового завершення процедури.
- Скористатися механізмом генерації та обробки винятків (виключних ситуацій), який є зараз у більшості ЯВУ. У цьому випадку в позаштатній ситуації код у вкладеному циклі збуджує виняток, а блок обробки винятків, в який поміщений весь вкладений цикл, перехоплює та обробляє його. Недоліком є реалізація механізму обробки винятків у більшості випадків така, що швидкість роботи програми зменшується. Правда, в сучасних умовах це не особливо важливо: практично втрата продуктивності настільки мала, що має значення лише для небагатьох додатків.
- Зрештою, існують спеціальні мовні засоби для виходу із вкладених циклів. Так, у мові Ада програміст може позначити цикл (верхній рівень вкладеного циклу) міткою, і команді дострокового завершення циклу вказати цю мітку. Вихід відбудеться не з поточного циклу, а зі всіх вкладених циклів до позначеного, включно.