русс | укр

Мови програмуванняВідео уроки php mysqlПаскальСіАсемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование


Linux Unix Алгоритмічні мови Архітектура мікроконтролерів Введення в розробку розподілених інформаційних систем Дискретна математика Інформаційне обслуговування користувачів Інформація та моделювання в управлінні виробництвом Комп'ютерна графіка Лекції


Цикли while та for


Дата додавання: 2014-11-28; переглядів: 882.


Ми вже зіткнулися з циклами while та for. У

while (вираз)

твердження

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

Твердження з for

for (вираз1; вираз2; вираз3)

твердження

є рівнозначним

вираз1;

while (вираз2) {

твердження

вираз3;

}

за винятком поводження continue, яке описано у Розділі 3.7.

Граматично, всі три частини циклу for являються виразами. Найчастіше вираз1 та вираз3 — це присвоювання або виклики функцій, тоді як вираз2 — порівнювальний вираз. Будь-яку з трьох частин можна пропустити, але крапка з комою повинні залишитись. Якщо вираз1 і вираз3опущено, тоді вони просто вилучаються з оцінювання. Якщо ж порівнювального виразу2 бракує, уся конструкція набирає значення завжди істинної, тож

for (;;) {

...

}

є нескінченим циклом, який ймовірно буде перервано іншим способом, скажімо за допомогоюbreak або return.

Використання while чи for у великій мірі залежить від власних вподобань. Так, наприклад у

while ((c = getchar()) == ' ' || c == '\n' || c == '\t')

; /* опустити пробіли */

відсутні ініціалізація, або реініціалізація, тож while тут — природніший.

Виразам із for надається перевага коли присутні проста ініціалізація та приріст, оскільки forзберігає вирази, що контролюють цикл, ближче один до одного, у полі зору на вершечку циклу. Це найбільш очевидно у

for (i = 0; i < n; i++)

що є ідіомою C для обробки перших n елементів масиву, аналогічно DO у Fortran або for у Pascal. Ці аналогії, проте, не є досконалими, оскільки індекс та межа циклу for у C можуть змінюватися зсередини циклу, крім того індексна змінна i утримує своє значення після того, як цикл завершився з якоїсь причини. Оскільки компоненти for можуть складатися з довільних виразів, цикли for не обмежуються арифметичними прогресіями. Тим не менше, використання непов'язаних обчисленнь в ініціалізації та прирості for вважається поганим стилем, їх краще приберегти для керівної частини циклу.

В якості більшого прикладу, ось ще одна версія atoi — програми для конвертації рядка у свій числовий еквівалент. Цей приклад більш узагальнений ніж той, що ви знайдете у Розділі 2, він справляється з можливими пробілами попереду і можливими знаками + та -. (Розділ 4 міститьatof, яка здійснює таке саме перетворення для чисел з рухомою точкою).

Структура програми відображає форму вводу:

пропустити пробіли, якщо такі є

здобути символ, якщо такий є

здобути десяткове значення і перетворити його

Кожний крок виконує тільки свою частину завдання і залишає решту незайманим для наступного кроку. Весь процес завершується на першому ж символі, що не може бути частиною числа.

#include <ctype.h>

 

/* atoi: перетворює s на ціле; 2-а версія */

int atoi(char s[])

{

int i, n, sign;

 

for (i = 0; isspace(s[i]); i++) /* пропустити пробіли */

;

sign = (s[i] == '-') ? -1 : 1;

if (s[i] == '+' || s[i] == '-') /* пропустити знаки */

i++;

for (n = 0; isdigit(s[i]); i++)

n = 10 * n + (s[i] - '0');

return sign * n;

}

Стандартна бібліотека передбачає складнішу функцію, strtol, для перетворення рядків у довгі цілі числа (загляніть до Розділу 5 Додатка Б).

Переваги зберігання керівних частин циклу разом ще очевидніші, коли маємо справу із декількома гніздованими циклами. Наступна функція — це сортування Шела, для впорядковування масиву цілих чисел. Головна ідея цього алгоритму сортування, який був винайдений Д.Л. Шелом у 1959 році, полягає в тому, що на початковій стадії порівнюються віддалені одне від одного елементи замість прилягаючих, на відміну від простіших взаємозамінних алгоритмів сортування. Це досить швидко усуває велику кількість невпорядкованості, тож пізнішим стадіям залишається менше роботи. Інтервал між порівнюваними елементами поступово зменшується до одного, в цьому пункті сортування перетворюється у звичайну методу переставляння прилеглих елементів.

/* shellsort: сортує v[0]...v[n-1] в зростаючій послідовності */

void shellsort(int v[], int n)

{

int gap, i, j, temp;

 

for (gap = n/2; gap > 0; gap /= 2)

for (i = gap; i < n; i++)

for (j = i - gap; j >= 0; && v[j] > v[j+gap]; j -= gap) {

temp = v[j];

v[j] = v[j+gap];

v[j+gap] = temp;

}

}

Ми бачимо три гніздованих цикли. Зовнішній контролює проміжок між порівнюваними елементами, звужуючи його починаючи з n/2 у два рази при кожному поступі, доти, доки проміжок не стане нульовим. Середній цикл проходиться по кожному елементові. Внутрішній цикл порівнює кожну пару елементів розділених проміжком gap і переставляє ті, що не знаходяться в послідовності зростання. Оскільки проміжок gap поступово скорочується до одного, всі елементи зрештою мають остаточно впорядкуватись. Зауважте, як загальність конструкції for дозволяє зовнішньому циклу набрати тієї самої форми, що й інші, навіть якщо це не є арифметичною прогресією.

Ще один, останній, оператор C, це кома «,», яка найчастіше знаходить застосування у твердженнях for. Пара виразів, розділених комою, розцінюються у послідовності з ліва на право. Тип і значення результату дорівнюватиме типу і значенню правого операнда. Таким чином, у твердженні for можна розмістити декілька виразів у різних частинах, наприклад для обробки двох індексів одночасно. Це ілюстровано функцією reverse(s), яка обертає ланцюжок s на місці.

#include <string.h>

 

/* reverce: обертає ланцюжок s на місці */

void reverse(char s[])

{

int c, i, j;

 

for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {

c = s[i];

s[i] = s[j];

s[j] = c;

}

}

Коми, що розділяють аргументи функцій чи змінні в оголошеннях тощо не являються операторами і не гарантують оцінювання з ліва на право.

Кома-оператори повинні використовуватись вибірково. Вони найбільш придатні для близько споріднених конструкцій, як показано у циклі for функції reverse, також ви знайдете їх у макросах, там де багатоетапні обчислення треба занотувати одним виразом. Вираз з комою може бути доречним також для взаємозаміни елементів у reverse, там де заміна може розглядатися як одна операція:

for (i = 0, j = strlen(s) - 1; i < j; i++, j--)

c = s[i], s[i] = s[j], s[j] = c;

Вправа 3-3. Напишіть функцію expand(s1, s2), яка розкриває скорочення на кшталт a-z в ланцюжкові s1 у відповідний повний список abc...xyz у s2. Дозвольте використання літер обох регістрів, а також цифр. Будьте готові до обробки таких випадків, як a-b-c, a-z0-9 або -a-z. Добийтеся того, щоб "-" попереду або вкінці розглядалася буквально.


<== попередня лекція | наступна лекція ==>
Else if | Цикли do-while


Онлайн система числення Калькулятор онлайн звичайний Науковий калькулятор онлайн