Як навучыць пераадольваць цяжкасці, а заадно і пісаць цыклы

Нягледзячы на ​​тое, што гаворка пойдзе пра адну з базавых тэмаў, дадзены артыкул напісаны для вопытных спецыялістаў. Мэта – паказаць якія памылкі бываюць у пачаткоўцаў у праграмаванні. Для практыкуючых распрацоўшчыкаў гэтыя праблемы ўжо даўно вырашаны, забытыя ці ўвогуле не заўважаныя. Артыкул можа спатрэбіцца, калі раптам вам давядзецца дапамагаць з гэтай тэмай каму-небудзь. У артыкуле праводзяцца паралелі з матэрыялам з розных кніг па праграмаванні аўтарства Шылдта, Страўструпа, Акулава.

Тэма пра цыклы абрана таму, што на ёй адсяецца даволі шмат людзей пры засваенні праграмавання.

Дадзеная методыка разлічана на слабых студэнтаў. Як правіла, моцныя на гэтай тэме не захрасаюць і асаблівых методык для іх прыдумляць не трэба. Другарадная мэта артыкула - перавесці гэтую методыку з класа "працуе на ўсіх студэнтах, але толькі ў аднаго выкладчыка" ў клас "працуе ва ўсіх студэнтаў, усіх выкладчыкаў". На абсалютную арыгінальнасць не прэтэндую. Калі вы ўжо прымяняеце падобную методыку для навучання гэтай тэме, напішыце, калі ласка, чым ваш варыянт адрозніваецца. Калі вырашыце прымяніць, раскажыце па выніках, як усё прайшло. Калі падобная методыка апісана ў якой-небудзь кніжцы, напішыце, калі ласка, назву.


Дадзеную методыку я адпрацоўваў 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 між іншым. Студэнт далей сам выбера, што яму больш падабаецца. Калі ж пераўтварэнні выклікаюць цяжкасці, то лепш не рассейваць увагу. Няхай спачатку студэнт вырашыць усё з дапамогай 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

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) уводзіцца з клавіятуры.

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

Ш=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

Дадаць каментар