Как да научим да преодолеваме трудности и заедно с това да пишем цикли

Въпреки че темата е базова, тази статия е написана за опитни специалисти. Целта е да покажем какви заблуждения имат новаците в програмирането. За практикуващите разработчици тези проблеми отдавна са решени, забравени или изобщо не се забелязват. Статията може да е полезна, ако трябва да помагате на някого с тази тема. В нея се правят паралели с материали от различни книги по програмиране, написани от Шилдт, Страуструп и Окулов.

Темата за циклите е избрана, защото често много хора отпадат при изучаването на програмирането.

Тази методика е предназначена за слаби студенти. Обикновено по-силните не засядат на тази тема и не е нужно да се създават специални методики за тях. Второстепенната цел на статията е да преведе тази методика от категорията "работи за всички студенти, но само за един преподавател" в категорията "работи за всички студенти и преподаватели". Нямам претенции за абсолютна оригиналност. Ако вече прилагате подобна методика, моля, споделете с какво вашият вариант се различава. Ако решите да я приложите, разкажете как усвоихте всичко. Ако подобна методика е описана в някоя книга, моля, напишете заглавието.


Тази методика съм отработвал в продължение на 4 години, занимавайки се индивидуално със студенти с различно ниво на подготовка. Общо около петдесет студенти и две хиляди часа занятия. Първоначално студентите трудно се справяха и често напускаха. След всеки студент, методиката и материалите бяха коригирани. През последната година студентите вече не се задържат на тази тема, така че реших да споделя опита си.

Защо толкова много букви? Циклите са елементарни!

Както вече казах, за практикуващите разработчици и за силните студенти, сложността на концепцията за цикли може да бъде недооценена. Например, може да се проведе дълга лекция, да се видят клатещи глави и умни погледи. Но при опит за решаване на задача, започва ступор и необясними проблеми. След лекцията при студентите вероятно остава само частично разбиране. Ситуацията се усложнява от факта, че студентите не могат сами да формулират какво точно е заблуждението им.
Един ден осъзнах, че студентите възприемат примерите ми като йероглифи. Тоест, като неделими части от текст, в които трябва да добавят някаква "магическа" буква, за да проработят.
Понякога забелязвах, че студентите мислят, че за решаването на конкретна задача е нужна някаква друга конструкция, за която просто още не съм им разказал. Въпреки че за решението беше нужно само да се модифицира примерът.

Затова стигнах до идеята, че основното внимание трябва да се обръща не на синтаксиса на изразите, а на идеята за рефакториране на повтарящия се код с помощта на цикли. Щом учениците усвоят тази идея, всеки синтаксис може да бъде научен чрез малки упражнения.

На кого и защо преподавам

Тъй като няма входни изпити, на занятията могат да присъстват както силни, така и много слаби студенти. По-подробно за моите студенти може да се прочете в статията Портрет на слушателите на вечерни курсове
Стремя се всеки, който иска, да усвои програмирането.
Моите занятия се провеждат индивидуално и студентът плаща парите си за всяко. Изглежда, че студенти биха се опитали да оптимизират разходите и да изискват минимум. Въпреки това, хората посещават присъствени занятия с жив преподавател не за самите знания, а за увереността, че са успели да научат, за усещането за напредък и за одобрението от експерта (преподавателя). Ако студентите не усещат напредък в обучението си, те ще се откажат. В общи линии, занятията могат да бъдат организирани така, че студентите да усещат напредък в увеличаването на познатите конструкции. Първо детайлно изучаваме while, след това изучаваме for, после do while и ето, имаме курс за хиляда и една нощ, в който за два месеца изучаваме само цикли, а на изхода - студент, който по диктовка написва стандартна библиотека. Но за да решават практически задачи, е нужно не само знание по материала, но и самостоятелност в неговото приложение и в търсенето на нова информация. Затова за присъствените курсове считам, че е правилен принципът - да научим на минимум и да насърчаваме самостоятелното изучаване на нюансите и свързаните теми. В темата за цикли, минимумът считам конструкцията while. На нея може да се разбере принципът. Знаейки принципа, може да се овладее и for, и do-while самостоятелно.

За да постигнете усвояване на материала от слаби студенти, просто описването на синтаксиса не е достатъчно. Нужно е да се дават повече прости, но разнообразни задачи и да се описват примери по-подробно. В крайна сметка скоростта на усвояване се ограничава от способността на студента да преобразува изрази и да търси закономерности. За съобразителните студенти повечето задачи ще бъдат скучни. При занятия с тях не трябва да се настоява за решаване на 100% от задачите. Моят материал може да се види на моят Github. Всъщност репозиториът по-скоро прилича на гримуар на чернокнижник — никой, освен мен, няма да разбере какво и къде се намира, а ако не преминете проверката, може да полудеете.

Методиката е насочена към практиката.

Теорията се обяснява на примера на решаване на задача. На занятията по основи на програмирането, където се изучават разклонения и цикли, просто не може да се проведе полезна лекция по една тема за цял час. 15-20 минути са достатъчни, за да се обясни концепцията. Основните затруднения се появяват при изпълнението на практически задачи.
Начинаещите преподаватели могат да представят оператори, разклонения, цикли и масиви в една лекция. Но студентите им ще се сблъскат с проблема да усвоят тази информация.
В крайна сметка не е достатъчно да се разкаже материал, трябва и да се уверим, че слушателите са го разбрали.

Фактът на усвояване на темата се определя по това как студентът се справя с самостоятелната работа.
Ако на студента е успяло да реши задача по темата без помощ от преподавателя, значи темата е усвоена. За да осигурим самостоятелна проверка, всяка задача има описана таблица с тестови сценарии. Задачите имат ясно изразен ред. Не се препоръчва пропускането на задачи. Ако текущата задача е твърде сложна, преминаването към следващата е безполезно. Тя е дори по-сложна. За да може студентът да се справи с текущата сложна задача, му се обясняват няколко трика на примера на първата задача. Всъщност, цялото съдържание на темата се свежда до трикове за преодоляване на трудности. Циклите са по-скоро страничен ефект.

Първата задача винаги е пример. Втората се различава незначително и се изпълнява "самостоятелно" веднага след първата под наблюдение на преподавателя. Всички последващи задачи са насочени към това да обърнат внимание на различни детайли, които могат да предизвикат заблуждения.

Обяснението на примера представлява диалог, в който студентът трябва да активира back propagation и крос-валидация, за да се увери, че е усвоил част от материала.

Ще бъда банален и ще заявя, че първият пример по темата е много важен. При наличие на материал за обширна самостоятелна работа, пропуските от първия пример могат да бъдат поправени. Ако освен примера няма нищо друго, студентът вероятно няма да усвои темата.

While или for?

Един от спорните въпроси е изборът на конструкцията за примера: while или for. Един път мой познат практикуващ разработчик без опит в преподаването ми убеждаваше цял час, че цикълът for е най-лесният за разбиране. Аргументите му бяха, че "в него всичко е ясно и подредено". Въпреки това, първопричината за затрудненията на истинските новаци е самата идея на цикъла, а не как да се напише. Ако човек не разбере тази идея, ще има затруднения с синтаксиса. След като идеята е осъзната, проблемите с оформлението на кода изчезват сами.

В моите материали темата за циклите следва темата за разклоненията. Външната прилика между if и while позволява да се направи пряка аналогия: "когато условието в заглавието е истинно, се изпълнява тялото". Особеността на цикъла е, че тялото се изпълнява много пъти.

Вторият ми аргумент е, че while изисква по-малко оформление отколкото for. По-малко оформление — по-малко глупави грешки с пропуснати запетаи и скоби. При новаците все още не е развита внимателността и педантичността, за да избягват автоматично синтактичните грешки.
Третият аргумент е, че в много добри книги while се обяснява първо.

Ако студентът лесно успява да преобразува изрази, може да се говори за for мимоходом. Студентът сам ще избере какво му харесва повече. Ако обаче преобразуванията създават затруднения, по-добре е да не се разсейва вниманието. Нека първо студентът реши всичко с помощта на while. Когато темата за циклите е усвоена, може да се пренапишат решенията, за да се упражни преобразуването от while в for.
Циклите с постусловие са доста рядкост. Не отделям време за тях изобщо. Ако студентът е усвоил идеите за установяване на закономерности и преобразуване на изрази, ще може да се справи и без моя помощ.

При демонстрацията на първия пример на силните студенти, обръщам внимание на това, че в първия пример е важно да се фиксира не само решението, но и цялата верига от действия, които са довели до резултата. Ленивите студенти могат да пренебрегнат писането и да запомнят само крайния алгоритъм. Трябва да ги убедим, че веднъж и за тях ще попадне сложна задача. За нейното решение, ще е необходимо да се премине по стъпките, както в този пример. Именно затова е важно да се фиксират всички етапи. В следващите задачи ще може да се остави само финалният вариант на решението.

Основна идея на автоматизацията е, че поверяваме на компютъра да извършва рутинната работа вместо човека. Един от основните приеми е написването на цикли. Той се използва, когато в програмата последователно се пишат няколко еднакви повтарящи се действия.

Явното е по-добро от неявното

Може да изглежда добра идея в първата задача за цикли да изведем на екрана някаква еднаква фраза няколко пъти. Например:

Ура, работи!
Ура, работи!
Ура, работи!
Ура, работи!
Ура, работи!
Ура, работи!
Ура, работи!
Ура, работи!

Този вариант е лош, защото в изхода не се вижда стойността на брояча. Това е проблем за начинаещите. Не бива да се подценява. В началото тази задача беше първа, а задачата за изход на ред от числа по възрастване — втора. Приходеше се да се въвеждат допълнителни термини „цикъл N пъти“ и „цикъл от A до B“, които по същество са едно и също. За да не се създават излишни същности, реших да покажа само примера с изход на ред от числа. Нямалко успяват без подготовка да научат да държат в главата си брояча и да моделират поведението на програмата в главата. Някои студенти се сблъскват с моделирането „в ума“ именно в темата за цикли.
След известно практикуване, задачата за повторение на еднакъв текст я давам за самостоятелно решаване. Ако първо даваш видим брояч, а след това невидим, то на студентите им възникват по-малко проблеми. Понякога е достатъчна подсказката „не пиши брояча на екрана“.

Как е обяснено от другите?

В повечето учебни материали в интернет, синтаксисът на цикъла се предоставя в комбинация с "лекция". Например, на developer.mozilla.org (в момента) в допълнение към цикъла while се описват и няколко други конструкции. При това се дават единствено самите конструкции под формата на шаблони. Резултатът от тяхното изпълнение се описва с думи, а илюстрация няма. Според мен, такава подача на темата намалява до нула полезността на тези материали. Ученикът може да препише кода и да го стартира сам, но референтен пример все пак е необходим. Как да се разбере, че примерът е преписан правилно, ако няма с какво да сравниш резултата?
Когато се предоставя само шаблон, без пример, на студента става още по-трудно. Как да се разбере, че фрагментите от кода са разположени правилно в шаблона? Може да се опита да напише по някакъв начин, а след това да го стартира. Но ако няма референтен пример за сравнение на резултата, то стартирането също не помага.

В курса по C++ на интуите синтаксисът на цикъла е скрит на третата страница на лекция 4 по темата "оператори". При обяснението на синтаксиса на циклите се акцентира особено на термина "оператор". Терминът се предоставя под формата на набор от факти като "символ; това е оператор", "{} е съставен оператор", "тялото на цикъла трябва да е оператор". Такъв подход не ми харесва, тъй като сякаш скрива важните взаимовръзки зад един термин. Разборът на изходния код на програмата на термини на такова ниво е нужен на разработчиците на компилатори за реализиране на спецификациите на езика, но съвсем не е необходим на студентите в началната фаза. Новаците в програмирането рядко притежават достатъчно усърдие, за да подходът толкова внимателно към термините. Редкият човек запомня и разбира новите думи от първия път. Практически никой не може правилно да приложи термин, който току-що е научил. Затова у студентите възникват много грешки, като например "написах 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

стринг temp;
инт R;
инт 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

Ако студентът е усвоил метода за замяна на променната, той ще се справи доста бързо. Възможният проблем отново ще е свързан с нулирането на променната. Ако не успява с преобразуването, значи сте се побързали и трябва да решавате по-прости задачи.

Благодаря за вниманието. Харесвайте, абонирайте се за канала.

P.S. Ако сте намерили печатни грешки или грешки в текста, моля, съобщете ми. Можете да го направите, като выделите част от текста и натиснете на Mac «⌘ + Enter», а на класически клавиатури «Ctrl / Enter», или чрез лични съобщения. Ако тези варианти не са налични, напишете за грешките в коментарите. Благодаря!

Само регистрирани потребители могат да участват в анкетата. Влезте, моля.

Анкета за читатели без карма

  • 20,0%Преподавам професионално, +12

  • 10,0%Преподавам професионално, -11

  • 70,0%Не преподавам, +17

  • 0,0%Не преподавам, -10

  • 0,0%Друго0

Гласували 10 потребители. Въздържат се 5 потребители.

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster