Як навчити долати труднощі, а заразом і писати цикли

Незважаючи на те, що мова піде про одну з базових тем, цю статтю написано для досвідчених фахівців. Мета — показати якісь помилки бувають у новачків у програмуванні. Для практикуючих розробників ці проблеми вже давно вирішені, забуті чи взагалі не помічені. Стаття може стати в нагоді, якщо раптом вам доведеться допомагати з цією темою комусь. У статті проводяться паралелі з матеріалом із різних книг із програмування авторства Шилдта, Страуструпа, Окулова.

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

Ця методика розрахована на слабких студентів. Як правило, сильні на цій темі не застрягають і особливих методик для них вигадувати не потрібно. Друга мета статті — перекласти цю методику з класу «працює на всіх студентах, але тільки в одного викладача» до класу «працює у всіх студентів, усіх викладачів». На абсолютну оригінальність не претендую. Якщо ви вже використовуєте схожу методику для навчання цієї теми, напишіть, будь ласка, чим ваш варіант відрізняється. Якщо вирішите застосувати, розкажіть, як усе минуло. Якщо схожа методика описана в якійсь книжці, напишіть, будь ласка, назву.


Цю методику відпрацьовував 4 роки, займаючись індивідуально зі студентами різного рівня підготовки. Загалом близько півсотні студентів та двох тисяч годин занять. Спочатку на цій темі студенти надовго застрягали та йшли. Після кожного студента методика та матеріали коригувалися. Останній рік студенти вже не застрягнуть на цій темі, тож я вирішив поділитися напрацюваннями.

Навіщо стільки букв? Цикли це ж просто!

Як я вже писав вище, для практикуючих розробників та для сильних студентів складність концепції циклів може бути недооцінена. Наприклад можна влаштувати довгу лекцію, побачити кивають голови та розумні очі. Але при спробі вирішити якесь завдання, починається ступор і незрозумілі проблеми. Після лекції у студентів, напевно, склалося лише часткове розуміння. Ситуація посилюється тим, що студенти не можуть самі озвучити, в чому саме їхня помилка.
Якось я усвідомив, що студенти сприймають мої приклади як ієрогліфи. Тобто як неподільні шматки тексту, в яких потрібно дописати якусь магічну букву і воно запрацює.
Іноді я помічав, що студенти думають, що для вирішення конкретного завдання потрібна якась ще конструкція, яку я ще не розповів. Хоча для рішення потрібно лише трохи модифікувати приклад.

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

Кому і навіщо я викладаю

Оскільки вступних іспитів немає, на заняттях можуть бути як сильні, так і дуже слабкі студенти. Детальніше про моїх студентів можна почитати у статті Портрет слухачів вечірніх курсів
Я прагнув того, щоб програмування освоювали всі, хто цього хоче.
Мої заняття проходять індивідуально, і студент платить свої гроші за кожне. Здавалося б, студенти оптимізуватимуть витрати та вимагатимуть мінімум. Однак люди ходять на очні заняття з живим викладачем не за самими знаннями, а за впевненістю в тому, що вони встигли засвоїти, за відчуттям прогресу та за схваленням від експерта (викладача). Якщо студенти не будуть відчувати прогрес у своєму навчанні, вони будуть йти. Загалом можна збудувати заняття так, щоб студенти відчували прогрес у збільшенні кількості знайомих конструкцій. Тобто спочатку детально вивчаємо while, потім вивчаємо for, потім do while і ось у нас готовий курс на тисячу й одну ніч, у якому два місяці вивчаються лише цикли, а на виході — студент, який під диктовку написав стандартну бібліотеку. Однак для того, щоб вирішувати практичні завдання, потрібно не тільки знання матеріалу, а й самостійність у його застосуванні та пошуку нової інформації. Тому для очних курсів вважаю правильним принцип — навчити мінімум і заохочувати самостійне вивчення нюансів та суміжних тем. У темі про цикли, мінімум я вважаю конструкцію while. На ній можна зрозуміти принцип. Знаючи принцип, можна освоїти і for і do-while самостійно.

Щоб досягти освоєння матеріалу слабкими студентами, описати синтаксис недостатньо. Потрібно давати більше простих, але різноманітних завдань і розписувати приклади більш детально. Зрештою швидкість освоєння обмежується здатністю студента до перетворення виразів та пошуку закономірностей. Для кмітливих студентів більшість завдань будуть нудними. При заняттях з ними можна не наполягати на вирішенні 100% завдань. Мій матеріал можна подивитися на моєму гітхабі. Правда репозиторій більше схожий на гримуар чаклуна — ніхто, крім мене, не зрозуміє, що де знаходиться, а якщо провалити перевірку, то можна збожеволіти.

Методика орієнтована на практику

Теорія пояснюється з прикладу розв'язання завдання. На заняттях з основ програмування, де вивчаються розгалуження та цикли, просто не вдасться влаштувати корисну лекцію з однієї теми на цілу годину. 15-20 хвилин вистачає, щоб пояснити концепцію. Основні складнощі з'являються під час виконання практичних завдань.
Викладачі-початківці можуть вивалити оператори, розгалуження, цикли та масиви за одну лекцію. Ось тільки студенти у них зіштовхнуться із проблемою засвоєння цієї інформації.
Адже треба не тільки розповісти матеріал, а й переконатися, що слухачі його зрозуміли.

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

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

Пояснення прикладу є діалогом, в якому у студента потрібно викликати back propagation і крос-валідацію щоб переконатися в засвоєнні порції матеріалу.

Буду банальний і заявлю, що перший приклад на тему — дуже важливий. За наявності матеріалу для великої самостійної роботи, упущення першого прикладу можна виправити. Якщо крім прикладу більше нічого немає, то студент, швидше за все, не освоїть тему.

While чи for?

Одне із спірних питань — вибір конструкції для прикладу: while чи for. Якось мій знайомий практикуючий розробник без досвіду викладання цілу годину переконував мене, що цикл for — найпростіший для розуміння. Аргументи зводилися до "в ньому все зрозуміло і по місцях розкладено". Проте причина труднощів справжніх новачків у ідеї циклу, а чи не у його написанні. Якщо людина не зрозуміє цю ідею, то у неї будуть труднощі із синтаксисом. Як тільки ідея усвідомлена, проблеми оформлення коду зникають самі.

У моїх матеріалах тема циклів слідує за темою для розгалуження. Зовнішнє подібність if і while дозволяє провести пряму аналогію: «коли умова в заголовку істинно, то виконується тіло». Особливість циклу лише тому, що тіло виконується багато разів.

Другий мій аргумент у тому, що при потребі менше оформлення, ніж для. Менше оформлення — менше дурних помилок із пропущеними комами та дужками. У новачків ще не настільки розвинена уважність і скрупульозність, щоб автоматично уникати синтаксичних помилок.
Третій аргумент — у багатьох добрих книгах while пояснюється першим.

Якщо студенту вдається легко перетворювати вирази, можна розповісти про for мимоходом. Студент далі сам вибере, що йому більше подобається. Якщо ж перетворення викликають труднощі, краще не розсіювати увагу. Нехай спочатку студент вирішить усе за допомогою while. Коли тема циклів освоєна, можна переписати рішення, щоб відпрацювати перетворення, коли в for.
Цикли з постумовою – досить рідкісний звір. На нього я час не витрачаю взагалі. Якщо студент освоїв ідеї виявлення закономірностей та перетворення виразів, то зможе розібратися без моєї допомоги.

При демонстрації першого прикладу сильним студентам звертаю увагу на те, що в першому прикладі важливо зафіксувати не лише рішення, а й увесь ланцюжок дій, що привели до результату. Ледачі студенти можуть знехтувати писаниною і перенести собі лише кінцевий алгоритм. Їх треба переконати в тому, що якось і для них трапиться складне завдання. Для її вирішення знадобиться йти кроками як у цьому прикладі. Саме тому важливо зафіксувати усі етапи. У наступних завданнях можна буде залишити лише фінальний варіант вирішення.

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

Явне краще за неявне

Може здатися гарною ідеєю в першому завданні циклів вивести на екран якусь однакову фразу кілька разів. Наприклад:

Ура, працює!
Ура, працює!
Ура, працює!
Ура, працює!
Ура, працює!
Ура, працює!
Ура, працює!
Ура, працює!

Такий варіант поганий тим, що у висновку не видно значення лічильника. Це проблема для початківців. Не варто її недооцінювати. Спочатку це завдання було першим, а завдання про виведення ряду чисел за зростанням — друге. Доводилося вводити додаткові терміни "цикл N разів" і "цикл від A до B", які по суті те саме. Щоб не плодити зайвих сутностей, я вирішив показувати лише приклад із виведенням ряду чисел. Дещо вдається без підготовки навчитися пам'ятати лічильник і моделювати поведінку програми в голові. Деякі студенти вперше стикаються з моделюванням «про себе» саме на темі про цикли.
Після деякої практики завдання на повторення однакового тексту я даю на самостійне рішення. Якщо давати спочатку видимий лічильник, а потім невидимий, то у студентів виникає менше проблем. Іноді достатньо підказки "не пиши лічильник на екран".

Як пояснюється в інших?

У більшості навчальних матеріалів в інтернеті синтаксис циклу дається у складі «лекції». Наприклад на developer.mozilla.org (зараз) разом із циклом while описуються ще кілька конструкцій. При цьому даються виключно самі конструкції у вигляді шаблонів. Результат їхнього запуску описується словами, а ілюстрація відсутня. На мій погляд, така подача теми примножує на нуль корисність таких матеріалів. Учень може переписати код і запустити його сам, але зразок для порівняння все одно потрібен. Як зрозуміти, що приклад переписаний правильно, якщо немає з чим порівняти результат?
Коли дається лише шаблон, без прикладу, студентові стає ще складніше. Як зрозуміти, що фрагменти коду розставлені у шаблоні правильно? Можна спробувати написати якосьа потім запустити. Але якщо немає зразка для порівняння результату, то запуск також не допоможе.

У курсі C++ на інтуїті синтаксис циклу закопаний у третій сторінці лекції 4 на тему «оператори». При поясненні синтаксису циклів наголошують на термін «оператор». Термін подається як набору фактів на кшталт «символ; це оператор», "{} це складовий оператор", "тіло циклу має бути оператором". Мені такий підхід не подобається тим, що він ховає важливі взаємозв'язки за одним терміном. p align="justify"> Розбір вихідного коду програми на терми на такому рівні потрібен розробникам компіляторів для реалізації специфікації мови, але ніяк не студентам у першому наближенні. Новачки в програмуванні рідко мають достатню прискіпливість, щоб настільки уважно ставитися до термінів. Рідкісна людина запам'ятовує і розуміє нові слова з першого разу. Практично ніхто не може правильно застосувати термін, який щойно дізнався. Тому у студентів виникає купа помилок на кшталт «написав while(a<7);{, а програма не працює».
На мій погляд, спочатку краще дати синтаксис конструкції відразу з дужками. Варіант без дужок пояснювати тільки якщо у учня виникло конкретне питання «чому тут без дужок і працює».

У книзі Окулова «Основи програмування» 2012 р. знайомство з циклами починається з шаблону for, потім даються рекомендації щодо його використання, а потім одразу йде експериментальний розділ заняття. Я так розумію, що книга писалася для тієї меншості дуже здібних учнів, які рідко приходять до мене на заняття.

У найпопулярніших книгах завжди пишеться результат фрагментів коду. Наприклад, у Шілдта «Java 8. Повне керівництво» 2015 року видання. Спочатку дається шаблон, потім приклад програми і відразу після нього результат виконання.

Як приклад розглянемо цикл while, у якому виконується зворотний
відлік, починаючи з 10, та виводиться рівно 10 рядків «тактів»:

//Продемонстрировать применение оператора цикла while
class While {
    public static void main(String args []) {
        int n = 10;
        while (n > 0) {
            System.out.println("такт " + n);
            n--;
        }
    }
}

Після запуску ця програма виводить десять тактів наступним чином:
такт 10
такт 9
такт 8
такт 7
такт 6
такт 5
такт 4
такт 3
такт 2
такт 1

Підхід із описом шаблону, прикладу програми та результату роботи цієї програми використовується також у книзі «Javascript для дітей» та в курсі js на w3schools.com. Формат веб-сторінки навіть дозволяє зробити цей приклад інтерактивним.

У книзі Страуструпа «Принципи та практика з використанням C++» 2016 р. автор пішов ще далі. Насамперед пояснюється, який результат має вийти, а вже після цього — показують текст програми. Причому за приклад беруть не просто випадкову програму, а дають екскурс в історію. Це допомагає звернути увагу на неї «Дивися, це не просто якийсь марний текст. Ти бачиш щось значуще».

Як приклад ітерації розглянемо першу програму, виконану на машині зі збереженою програмою (EDSAC). Вона була написана Девідом Уілером (David Wheeler) у комп'ютерній лабораторії Кембриджського університету (Cambridge University, England) 6 травня 1949 року. Ця програма обчислює та роздруковує простий список квадратів.
0 0
1 1
2 4
3 9
4 16
...
98 9604
99 9801

Тут у кожному рядку міститься число, за яким йдуть знак табуляції ('t') та квадрат цього числа. Версія цієї програми мовою C++ виглядає так:

//Вычисляем и распечатываем таблицу квадратов чисел 0-99
int main()
{
    int i = 0; // Начинаем с нуля
    while(i < 100){
        cout << i << 't' << square(i) << 'n';
        ++i;
    }
}

Цікаво, що шаблон синтаксису у цій книзі не описується. Страуструп у керівництві інструктора (переклад) наголошує на тому, що поважає інтелект своїх студентів. Можливе вміння виявити шаблон у кількох прикладах і є проявом такого інтелекту.

Як пояснюю я сам

Підхід Страуструпа: опис результату, потім розв'язання завдання, а потім самостійний аналіз студентом виглядає найпродуманішим. Тому я вирішив взяти за основу саме його, але розповідати на менш історичному прикладі — завдання щодо виведення «змісту». Вона формує відомий якір, щоб потім говорити «згадай завдання про зміст» і щоб студенти згадували саме її. У своєму прикладі я постарався попередити ще дві з найпоширеніших помилок. Далі я напишу про них докладніше.

На цьому задачі ми знайомимося із прийомами розв'язання складних завдань. Початкове рішення потрібно зробити примітивно та просто. А потім можна подумати, як поліпшити це рішення.
Введение
Глава 1
Глава 2
Глава 3
Глава 4
Глава 5
Глава 6
Глава 7
Заключение

За моїми спостереженнями, підхід «шаблон-приклад-результат» у різних комбінаціях все одно призводить до того, що студенти сприймають цикл як ієрогліф. Це виявлялося в тому, що вони не розуміли навіщо там писати умову, як вибирати між i++ та i- та інші начебто очевидні речі. Щоб уникнути цих помилок, підхід до розповіді про цикли повинен підкреслювати зміст повторення однакових дій і лише потім - оформлення їх за допомогою конструкції. Тому, перш ніж давати синтаксис циклу, потрібно вирішити завдання «в лоб». Примітивне рішення задачі щодо змісту виглядає так:

Console.WriteLine("Введение");
Console.WriteLine("Глава 1");
Console.WriteLine("Глава 2");
Console.WriteLine("Глава 3");
Console.WriteLine("Глава 4");
Console.WriteLine("Глава 5");
Console.WriteLine("Глава 6");
Console.WriteLine("Глава 7");
Console.WriteLine("Заключение");

Як його можна покращити?
Замінити однакові на цикл.
Які події тут повторюються поспіль без змін?
У цьому вся фрагменті таких немає. Втім, команди з висновку слова «Глава» з номером дуже схожі одна на одну.
Тому наступний етап – пошук різниці між фрагментами. Це тільки в цьому завдання все очевидно, потім повторюватимуться не одиночні команди, а блоки коду по 5 рядків і більше. Шукати доведеться не просто у списку команд, а конструкціях розгалуження чи циклу.
У прикладі різниця між командами серед слова після слова «Глава».
Щойно різниця знайдена, потрібно зрозуміти закономірність зміни. Відрізняється фрагмент це число? Воно постійно збільшується чи зменшується? Як змінюється значення числа між двома командами?
У прикладі число після слова "Глава" збільшується з кроком 1. Різниця знайдена, закономірність виявлена. Тепер можна замінити фрагмент, що розрізняється, на змінну.
Оголошувати таку змінну потрібно перед першим із фрагментів, що повторюються. Таку змінну зазвичай називають I або j або якось більш розгорнуто. Її початкове значення має дорівнювати першому значенню, що виводиться на екран. У прикладі перше значення 1.
Яке початкове значення слід взяти для виведення ряду чисел «100, 101, 102, 103, 104, 105»?
У цьому ряду перша кількість 100.
Після кожної команди виведення потрібно збільшити значення цієї змінної на 1. Ця одиниця є кроком зміни.
Який крок буде серед чисел «100, 102, 104, 106»?
У цьому ряду крок 2.
Після заміни фрагмента, що розрізняється на змінну, код буде виглядати так:

Console.WriteLine("Введение");
int i;
i = 0;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Заключение");

Після застосування прийому «виразити закономірність змінної» у коді виходить кілька груп однакових дій, що йдуть поспіль. Тепер повторювані дії можна замінити цикл.

Послідовність розв'язання задачі, де потрібно використовувати цикли, складається з етапів:

  1. Вирішити «в лоб» безліччю окремих команд
  2. Знайти закономірність
  3. Виразити закономірність змінної
  4. Оформити у вигляді циклу

Далі вводиться нові терміни, щоб студент не опинився у ситуації «все розумію, але сказати не можу»:
- Лічильник - завжди змінна, яка потрібна для відстеження кількості кроків циклу. Зазвичай, ціле число, яке порівнюється з обмеженням.
- Крок лічильника - опис закономірності зміни лічильника.
- Обмеження - число або змінна, з якою порівнюється лічильник, щоб алгоритм був кінцевим. Значення лічильника змінюється так, щоб наближатися до обмеження.
- Тіло циклу - набір команд, які повторюватимуться. Коли йдеться «команда написана всередині циклу», то мають на увазі саме тіло.
- Ітерація циклу - одноразове виконання тіла циклу.
- Умова циклу - логічний вираз, від якого залежить, чи буде виконуватися ще одна ітерація. (Тут можлива плутанина з конструкціями розгалуження)
Потрібно бути готовим до того, що спочатку студенти будуть застосовувати терміни не за призначенням. Це стосується як сильних, так і слабких. Налагодження спільної мови це мистецтво. Зараз напишу коротко: потрібно ставити завдання «виділи фрагмент коду з термін» і самому правильно використовувати ці терміни в розмові.
Після перетворення з циклом виходить фрагмент:

Console.WriteLine("Введение");
int i = 0;
while (i < 7) {
    Console.WriteLine("Глава " + i);
    i = i + 1;
}
Console.WriteLine("Заключение");

головне оману

Одна популярна помилка студентів у тому, що вони вміщують усередині конструкції циклу такі дії, які потрібно робити лише один раз. Наприклад, ось так:

;
int i = 0;
while (i < 7) {
    Console.WriteLine("Введение")
    Console.WriteLine("Глава " + i);
    i = i + 1;
    Console.WriteLine("Заключение");
}

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

Скільки разів потрібно повторювати написання команди: один чи багато разів?

Команди виведення слів «Вступ» та «Висновок», а також оголошення та ініціалізація змінної i не схожі на інші дії, що повторюються. Вони виконуються лише по одному разу, отже, їх потрібно писати за межами тіла циклу.

У коді повинні залишитися всі три етапи рішення, щоб потім посилатися на них у разі труднощів. Перші два варіанти достатньо закоментувати, щоб вони не заважали.
Увага студента слід звернути на такі факти:
— За умови циклу зазвичай порівнюється лічильник та обмеження. Лічильник може змінюватись у тілі циклу, а обмеження – ні. Щоб порушити це правило, потрібно сформулювати вагомі причини.
— Команди для виведення слів «Вступ» та «Висновок» знаходяться за межами тіла циклу. Нам потрібно їх виконати 1 раз. "Введення" - до повторення дій, "Висновок" - після.
У процесі закріплення цієї теми, освоєння наступних, а також розглядів із труднощами навіть сильним студентам корисно ставити запитання: «А ось цю дію скільки разів потрібно виконувати? Один чи багато?».

Розвиток додаткових навичок

У процесі вивчення циклів, у студентів ще тренується навичка діагностики та вирішення проблем. Для проведення діагностики студенту потрібно подати бажаний результат і порівняти його з фактичним результатом. Від різниці між ними залежать дії виправлення.
Оскільки студенти цьому етапі ще погано уявляють собі «бажаний» результат, вони можуть орієнтуватися на тестові дані. Як правило, ніхто на цьому етапі ще не розуміє, що може піти не так і як з цим боротися. Тому я даю під запис у зошит опис типових проблем та кілька способів їх вирішення. Вибір найбільше з них — завдання самого студента.
Запис потрібний щоб запитувати «вийшло те, що очікувалося?», «Яка з цих ситуацій зараз вийшла?», «Чи допомогло застосоване рішення?».

  1. Кількість дій на 1 менше чи більше, ніж очікується. Способи розв'язання:
    - Збільшити початкове значення лічильника на 1.
    - Замінити суворий оператор порівняння (< або >) на нестрогий (<= або >=).
    - Змінити значення обмеження на 1.
  2. Дії у циклі виконуються без зупинки, нескінченно. Способи розв'язання:
    - Додати команду зміни лічильника, якщо вона відсутня.
    — виправити команду зміни лічильника так, щоб його значення ставало ближчим до обмеження.
    - Прибрати команду зміни обмеження, якщо вона в тілі циклу.
  3. Кількість дій у циклі більш ніж на 1 менша або більша, ніж очікувалося. Дія в циклі не виконалася жодного разу. Спочатку необхідно з'ясувати фактичні значення змінних безпосередньо перед початком циклу. Способи розв'язання:
    - Змінити початкове значення обмеження
    - Змінити початкове значення лічильника

Зазвичай проблема 3 пов'язана з використанням не тієї змінної або необнулення лічильника.

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

  1. В якій обмеження, початкове значення лічильника або крок лічильника вводиться користувачем.
  2. У якій значення лічильника потрібно використовувати у якомусь арифметичному вираженні. Бажано з лічильником у підкореному виразі чи знаменнику, щоб різниця була нелінійна.
  3. У якій значення лічильника не виводиться на екран у процесі циклу. Наприклад виведення потрібної кількості однакових фрагментів тексту або намалювати фігуру черепашою графікою.
  4. У якій потрібно виконати спочатку одні дії, що повторюються, а потім інші.
  5. У якій потрібно виконати інші дії до і після повторюваних

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

Щоб зрозуміти, наскільки швидко можна рухатися, потрібно дати прочитати умови цих завдань і запитати: «Чим вони відрізняються від прикладу?», «Що потрібно змінити в прикладі, щоб їх вирішити?». Якщо студент осмислено відповідає, то нехай вирішить хоча б одну на занятті, а решта — вдома самостійно. Якщо рішення буде успішним, то можна почати пояснення про умови всередині циклів.
Якщо з самостійним рішенням труднощі, потрібно все відпрацьовувати на занятті. Щоб розв'язання задачі не нагадувало малювання сови, я рекомендую спочатку розв'язати задачу не універсально. Тобто так, щоб рішення проходило перший тест і не використовувало конструкцію циклу. Ну а потім вже застосовувати перетворення, щоб досягти універсальності рішення.

Цикли та розгалуження

На мою думку, корисно дати тему «цикли всередині розгалужень» окремо. Так, щоб потім було видно різницю між багаторазовою перевіркою умови та одноразовою.
Завдання для закріплення будуть виведені чисел від А до В, які вводяться користувачем:
- Завжди за зростанням.
— за зростанням або за зменшенням залежно від значень А та В.

До теми «розгалуження всередині циклів» потрібно переходити тільки після того, як студент освоїв прийоми: «заміна закономірності на змінну» і "заміна дій на цикл, що повторюються".
Головна причина застосування розгалужень усередині циклів – аномалії в закономірності. У середині вона порушується залежно від вихідних даних.
Тим студентам, які здатні шукати рішення шляхом комбінування простих прийомів, достатньо сказати «розгалуження можна писати всередині циклів» та дати завдання «для прикладу» повністю на самостійне рішення.
Завдання для прикладу:

Користувач вводить число Х. Вивести до стовпчика числа від 0 до 9 і поставити знак '+' навпроти того числа, яке дорівнює Х.

Якщо було введено 00+
1
2
3
4
5
6
7
8
9

Якщо було введено 60
1
2
3
4
5
6+
7
8
9

Якщо було введено 90
1
2
3
4
5
6
7
8
9+

Якщо було введено 7770
1
2
3
4
5
6
7
8
9

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

string temp;
temp = Console.ReadLine();
int x;
x = int.Parse(temp);
if (x==0) {
    Console.WriteLine(0 + "+");
} else {
    Console.WriteLine(0);
}
if (x==1) {
    Console.WriteLine(1 + "+");
} else {
    Console.WriteLine(1);
}
if (x==2) {
    Console.WriteLine(2 + "+");
} else {
    Console.WriteLine(2);
}
if (x==3) {
    Console.WriteLine(3 + "+");
} else {
    Console.WriteLine(3);
}
if (x==4) {
    Console.WriteLine(4 + "+");
} else {
    Console.WriteLine(4);
}
if (x==5) {
    Console.WriteLine(5 + "+");
} else {
    Console.WriteLine(5);
}
if (x==6) {
    Console.WriteLine(6 + "+");
} else {
    Console.WriteLine(6);
}
if (x==7) {
    Console.WriteLine(7 + "+");
} else {
    Console.WriteLine(7);
}
if (x==8) {
    Console.WriteLine(8 + "+");
} else {
    Console.WriteLine(8);
}
if (x==9) {
    Console.WriteLine(9 + "+");
} else {
    Console.WriteLine(9);
}

можливий

string temp;
temp = Console.ReadLine();
int x;
x = int.Parse(temp);
if (x==0) {
    Console.WriteLine("0+n1n2n3n4n5n6n7n8n9");
}
if (x==1) {
    Console.WriteLine("0n1+n2n3n4n5n6n7n8n9");
}
if (x==2) {
    Console.WriteLine("0n1n2+n3n4n5n6n7n8n9");
}
if (x==3) {
    Console.WriteLine("0n1n2n3+n4n5n6n7n8n9");
}
if (x==4) {
    Console.WriteLine("0n1n2n3n4+n5n6n7n8n9");
}
if (x==5) {
    Console.WriteLine("0n1n2n3n4n5+n6n7n8n9");
}
if (x==6) {
    Console.WriteLine("0n1n2n3n4n5n6+n7n8n9");
}
if (x==7) {
    Console.WriteLine("0n1n2n3n4n5n6n7+n8n9");
}
if (x==8) {
    Console.WriteLine("0n1n2n3n4n5n6n7n8+n9");
}
if (x==9) {
    Console.WriteLine("0n1n2n3n4n5n6n7n8n9+");
}

Подібне завдання я даю заздалегідь, під час вивчення теми для розгалуження.
Якщо у студента вийшов «можливий» варіант, то треба розповісти, що рішень однієї і тієї ж задачі може бути безліч. Проте вони відрізняються стійкістю змін вимог. Поставте запитання: «Скільки місць у коді потрібно буде виправити, якщо доведеться додати ще одне число?» У "можливому" варіанті потрібно буде додати ще одне розгалуження і дописати в 10 інших місцях нове число. У «бажаному» достатньо додати лише одне розгалуження.
Поставте завдання відтворити «бажаний» варіант, потім знайти у коді закономірність, виконати заміну змінної та написати цикл.
Якщо у вас є ідея, як вирішити це завдання без циклу якимось способом, напишіть, будь ласка, в коментарях.

Цикли всередині циклів

У цій темі слід звернути увагу на те, що:
- Лічильники для внутрішнього і зовнішнього циклу повинні бути різними змінними.
- Лічильник для внутрішнього циклу потрібно обнулювати багато разів (тобто в тілі зовнішнього циклу).
— у завданнях виведення тексту не можна спочатку написати одну літеру в кількох рядках, а потім другу. Потрібно спочатку вивести всі літери першого рядка, потім усі літери другого і таке інше.

Пояснення теми про цикли всередині циклів краще почати з пояснення важливості обнулення лічильника.
Завдання для прикладу:

Користувач вводить два числа: R і T. Вивести два рядки символів "#". У першому рядку має бути R штук символів. У другому рядку T штук. Якщо якесь число буде негативним, вивести повідомлення про помилку.

R=5, T=11#####
#############

R=20, T=3#####################
# # #

R=-1, T=6Значення R має бути невід'ємним

R=6, T=-2Значення T має бути невід'ємним

Очевидно, що це завдання теж має як мінімум два варіанти вирішення.
Бажаний

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
int i = 0;
while (i < R)
{
    Console.Write("#");
    i = i + 1;
}
Console.WriteLine();
i = 0;
while (i < T)
{
    Console.Write("#");
    i = i + 1;
}

Можливий №1

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
int i = 0;
while (i < R)
{
    Console.Write("#");
    i = i + 1;
}
Console.WriteLine();
int j = 0;
j = 0;
while (j < T)
{
    Console.Write("#");
    j = j + 1;
}

Відмінність у тому, що у «можливому» рішенні для виведення другого рядка була використана друга змінна. Потрібно наполягти на застосуванні однієї й тієї ж змінної обох циклів. Аргументувати таке обмеження можна тим, що рішення з одним лічильником двох циклів буде ілюстрацією терміна «обнулення лічильника». Розуміння цього терміна необхідне під час вирішення наступних завдань. Як компроміс, можна зберегти обидва рішення задачі.

Типова проблема з використанням однієї змінної-лічильника для двох циклів проявляється так:
R=5, T=11#####
######

Кількість символів у другому рядку не відповідає значенню T. Якщо з цією проблемою потрібна допомога, то потрібно «тиснути носом» у конспект про типові проблеми з циклами. Це симптом №3. Діагностується, якщо додати висновок значення лічильника безпосередньо перед другим циклом. Виправляється обнуленням. Але це краще одразу не розповідати. Студент повинен спробувати сформулювати хоча б одну гіпотезу.

Є, звісно, ​​ще такий варіант вирішення. Але я його у студентів жодного разу не бачив. На етапі вивчення циклів розповідь про нього розсіюватиме увагу. Можна повернутись до нього пізніше, при вивченні функцій роботи з рядками.
Можливий №2

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
Console.WriteLine(new String('#', R));
Console.WriteLine(new String('#', T));

Наступне обов'язкове завдання:

Виведіть на екран цифри від 0 до 9. Кожна цифра має бути на своєму рядку. Кількість цифр у рядку (W) вводиться з клавіатури.

W=10
1
2
3
4
5
6
7
8
9

W=100000000000
1111111111
2222222222
3333333333
4444444444
5555555555
6666666666
7777777777
8888888888
9999999999

Якщо студент освоїв прийом із заміною змінної, він впорається досить швидко. Можлива проблема знову в обнуленні змінної. Якщо не справляється з перетворенням, значить ви поспішили і необхідно вирішити найпростіші завдання.

Дякую за увагу. Ставте лайки, підписуйтесь на канал.

PS Якщо ви знайшли помилки або помилки у тексті, будь ласка, повідомте мені. Це можна зробити виділивши частину тексту і натиснувши в Mac ⌘ + Enter, а на класичних клавіатурах Ctrl / Enter або через особисті повідомлення. Якщо ці варіанти недоступні, напишіть про помилки в коментарях. Дякую!

Тільки зареєстровані користувачі можуть брати участь в опитуванні. Увійдіть, будь ласка.

Опитування для читачів без карми

  • 20,0%Викладаю професійно, +12

  • 10,0%Викладаю професійно, -11

  • 70,0%Не викладаю, +17

  • 0,0%Не викладаю, -10

  • 0,0%Інше0

Проголосували 10 користувачів. Утрималися 5 користувачів.

Джерело: habr.com

Додати коментар або відгук