×
Вы действительно хотите открыть доступ к тестированию по курсу C# 5.0 Стартовый на 40 дней?
ВИДЕОУРОК №6. Циклические конструкции в C#
Здравствуйте! Тема нашего сегодняшнего урока: «Циклические конструкции». Изучив материал данного занятия мы с вами сможем понимать работу циклических операторов, понимать работу операторов безусловного перехода, научимся применять циклические конструкции такие как while, do-while, for. Заметьте, их три. Четвертую конструкцию foreach мы будем рассматривать на следующем курсе. Потому что она работает с достаточно сложными другими конструкциями, такими как коллекции. Также мы научимся работать со вложенными циклами и научимся понимать работу циклов с охраняемыми ветвями. Был такой математик Дейкстра, который и разработал вот такую интересную разновидность циклов. Со всем этим мы с вами познакомимся сегодня.
И давайте сейчас перейдем к первому сладу и посмотрим как же циклическая конструкция выглядит формально. Здесь мы видим аналог русского алгоритмического языка (РАЯ) академика Ершова. Смотрим, у нас имеется слово ПОКА, обратите внимание, до тех пор ПОКА условие удовлетворяет истинности мы будем входить в тело цикла и выполнять определенную серию инструкций. Когда мы дошли до конца тела цикла мы автоматически возвращаемся на проверку. Проверяем, ЕСЛИ условие продолжает удовлетворять истинности, ТО мы продолжаем выполнять тело цикла и снова входит сюда. Вы спросите, как же может условие цикла изменятся. Очень просто, у нас могут быть какие-то переменные, которые будут проверятся здесь в условии а теле цикла мы можем изменять эти переменные. То есть такая примерная схема работы всех циклов. Каждый цикл имеет какое-то свое ключевое слово, которое определяет тип этого цикла, то ли это while, то ли это do-while, то ли это for, у нас здесь несколько циклов. Далее идет блок условия где мы проверяем, удовлетворяет ли условие истинности для того, чтобы либо войти в тело цикла и выполнять его, либо не выполнять. Обратите внимание, цикл – это некая конструкция, которая предназначена для многократного выполнения одной и той же серии инструкций. И та серия инструкций будет выполнятся много раз вот так витками раз, два, три, четыре до тех пор, пока условие не перестанет удовлетворять истинности. Один вот такой виток цикла принято называть итерацией. Но мы не будем много говорить по этому слайду, мы перейдем немного дальше. Перейдем уже к рассмотрению циклов. Но прежде чем мы начнем рассматривать настоящие циклы, давайте посмотрим один интересный оператор, который нам уже встречался, это оператор goto. Обратите внимание, что делает оператор goto, видите он здесь в нашем коде. У нас имеется некая метка с двоеточием и называется она Label, но не переживайте можно взять и назвать ее вашим или моим именем. И вот здесь goto как по-английски переводится? Иди туда. Куда? На Alex. Иди на Alex. Обратите внимание, что когда процессор у нас выполняет, мы сейчас будем это отлаживать, шагать будем, идем, идем, выполняем эту инструкцию, выводим на экран строку “hello”. У нас встречается goto и мы идем на Alex. Потом снова же выводим на экран «Hello», ага, снова goto, снова на Alex. А это программа когда-нибудь завершится? Нет, она никогда не завершится. Почему? Потому что здесь у нас организована вот такая циклическая последовательность переходов. Давайте мы вернем презентацию в нормальный вид. И перейдем теперь к программному коду, посмотрим как в программном коде у нас работает такая конструкция. Заходим в первый пример.
1: using System; 2: 3: // Оператор безусловного перехода - goto. 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: 12: // Label - это метка, на которую будет произведен переход оператором goto. 13: 14: Label: Console.WriteLine("Hello!"); 15: goto Label; 16: 17: } 18: } 19: }
И смотрим. На 14 строке мы создаем метку перехода с именем Label и пишем инструкцию языка C#. Главное, чтобы серия инструкций шла за меткой. А теперь давайте посмотрим, на 16 строке мы видим конструкцию goto? Снова же goto переводится как? Перейти куда-то. Именно на Label. Нажмем F11. Мы что делаем? Мы выводим на экран, потом попадаем на goto. Еще раз, еще раз… Смотрите, я постоянно нажимаю F11, и у нас выполняются эти переходы. Постоянно идет один и тот же вывод, то есть получается, что если мы запустим такую программу, то она будет бесконечно выводить строку «Hello». Хорошо, этот пример просто показывает работу оператора goto. Мы уже понимаем что goto может использоваться и в других программах, мы уже его встречали. Мы его встречали в swich-case и куда он переходил? На какой-то оператор case с определенным постоянным выражением. Помните кофейный аппарат мы с вами смотрели. Замечательно. А мы идем дальше и смотри следующий пример с goto. Что здесь у нас имеется?
1: using System; 2: 3: // Оператор безусловного перехода - goto. 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: int counter = 0; 12: 13: Label: 14: counter++; 15: Console.WriteLine("Counter = {0}", counter); 16: 17: if (counter < 3) 18: { 19: goto Label; 20: } 21: 22: Console.WriteLine("End"); 23: 24: // Delay. 25: Console.ReadKey(); 26: } 27: } 28: }
В теле метода Main на 11 строке, мы уже скоро подойдем к методам. На 11 строке мы создаем переменную counter и присваиваем ей значение 0. На 13 строке мы создаем метку переходя с именем Label. Давайте проверим, если я подставлю свое имя, все ли будет правильно. Так тут уже goto красным подчеркнуло. То есть вы видите, метка перехода, она, как и переменная может иметь какое-то осознанное название, осознанный, говорящий о чем-то идентификатор. И как вы видели, процессор у меня останавливается на этой метке. Это всего лишь некий виртуальный адрес, на который нужно перейти. Все инструкции в нашей программе они располагаются по определенным адресам. И для того, чтобы нам перейти, помните память как выглядит, для того чтобы нам перейти на какую-то инструкцию, нам нужно указать адрес. Но мы не хотим запоминать сложные адреса байтов памяти. Потому что позволяют нам языки программирования – присваивать этим байтам. То ли байт хранит данные, то ли байт хранит машинные инструкции, присваивать этим байтам удобочитаемые человеком имена, чтобы нам не манипулировать вот такими адресами. Как вам такие адреса: AF587AC8. Вы скажете, что это ужасно работать с такими адресами. И если у нас программа будет в таких адресах, это будет плохо. Еще раз, на 11 строке мы создаем переменную типа counter – счетчик итераций, сколько мы будем делать переходов. Теперь смотрим, мы создаем метку перехода Label, получается, что на следующую за ней инструкцию будет производится переход оператором goto. На 14 строке мы увеличиваем counter на 1. Так, если он был 0, теперь он стал 1. Выводим значение счётчика на экран и на 17 строке в условной конструкции if мы указываем выражение counter < 3. Чему равен counter? 1, а 1 меньше 3. Конечно же условие удовлетворяет истинности и мы входим в тело условной конструкции и выполняем операцию перехода на метку перехода. На 22 строке мы выводим на экран «End». Это такая строка-вывод, которая будет оттенять момент окончания серии переходов. Давайте шагать. Интересно посмотреть, как работает эта программа. Мне самому очень интересно. Так, counter, заметьте, counter = 0, пока counter еще равен 0, как это называется? Инкремент – это увеличение на 1, и инкременты бывают префиксные и постфиксные. Еще раз, на 14 строке мы увеличиваем значение счетчика на 1, смотрим F11. Так 1, сейчас мы выведем на экран, вывели, смотрим. Вот, уже есть. И теперь мы стоим в условии условной конструкции if. Counter = 1, а 3 = 3. И знак меньше что нам вернет? counter < 3 = true. Мы входи в тело условной конструкции, и мы сейчас делаем переход на метку перехода, а точнее куда? На ту инструкцию, которая помечена инструкцией перехода. Просто у нас метка выше, а можно поставить на одной строке. Просто так более лучше вид. F11, смотрите, мы перешли на метку. Counter = 1, теперь он равен 2, вывели два. Теперь мы опять стоим в условной конструкции, условие удовлетворяет истинности? Да, входим в тело условной конструкции, еще раз переходим. 3 меньше 3 = false. Входим в условную конструкцию? Нет. Обратите внимание, мы перешли на 22 строку. Она еще не выполнилась, но сейчас она выполнится. Мы видим, что мы трижды вывели эту строку на экран, которая на 15 строке в коде. Вы видите? Мы выводили разные значения счетчика итераций, вот этого, организованного нами цикла при помощи оператора безусловного перехода goto. Заметьте, он называется оператором безусловного перехода. И поэтому, если мы хотим создать какое-то условие, то мы понимаем, что этот оператор есть смысл использовать только в связке с некоторыми условными конструкциями, чтобы в зависимости от какого-то условия либо делать переход, либо не делать, потому что в предыдущей программе мы с вами видели, что эта программа иррациональна, она замкнулась в бесконечный цикл, она иррациональна и опасна. Хорошо, и теперь мы с вами еще раз нажимаем F11, возвращаемся, видите End, вот он вывелся, 22 строка сработала. Теперь хочется установить еще немного об операторе goto. Многие программисты считают, что операторы goto использовать плохо, потому что так посчитал математик Дейкстра. Он сказал, что оператор goto он разрушает правильный подход к программированию, к процедурному программированию, и он возвращает нас в варварские времена, когда мы сидели и программировали на ассемблере и игрались вот этими переходами. Вперед по коду, дальше в область старших адресов, либо в область младших адресов, представьте, что мы можем прыгать либо наверх, либо в область старших адресов. И во до сих пор все спорят хороший ли оператор goto или плохой. Меня тоже часто об этом спрашивают. Я говорю, что ну вот представьте себя специалистом, например профессиональным спортсменом, и тут завтра министр спорта говорит: «Мы закрываем такой вид спорта как гимнастика и фигурное катание, потому что это опасно. Там надо садится на шпагат, там надо делать какие-то сальто. Вот люди на улице пробуют это делать, падают, бьются головой об асфальт, рвут себе связки, попадают в травматологию». Ну так подождите, есть люди, которые профессионалы, которые делают это профессионально и красиво, и они знают, как это делать, это же не значит, что с прыжка садится на шпагат опасно, то это должен каждый делать, или взять и вообще запретить шпагат. Также и оператор goto, если вы считаете себя профессиональным программистом, если вы можете контролировать свой код, то почему бы и не использовать в определенный случаях. Тем более дальше мы посмотрим, Microsoft в своем фреймворке, они достаточно активно используют оператор goto, и при работе с, я не буду сейчас говорить об этих конструкциях, мы на следующих курса посмотрим, и я сделаю акцент на этом операторе. Не надо смотреть на goto вульгарно-прямолинейно. Он должен быть и он часто нам помогает. Но знайте, что он является спорным оператором и не спорьте с другими, если с вами начинают спорить. Но в своём коде, в первое время, пока вы начинающие, его использовать как можно реже. А мы с вами переходим дальше. То есть мы видим, что в данном примере у нас goto перебрасывает наверх, на 13 строку. Мы организовали вот такой цикл. Давайте зайдем в следующий пример.
1: using System; 2: 3: // Оператор безусловного перехода - goto. 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: bool condition = false; 12: 13: if (condition == true) 14: { 15: goto Label; 16: } 17: 18: Console.WriteLine("First line"); 19: 20: Label: 21: Console.WriteLine("Second line"); 22: 23: // Delay. 24: Console.ReadKey(); 25: } 26: } 27: }
Обратите внимание, на 11 строке мы создаем булеву переменную с именем condition. На 13 строке в условной конструкции if мы указываем выражение condition == true. Если условие удовлетворяет истинности мы входим в тело условной конструкции и делаем переход на 20 строку, на метку перехода Label. Обратите внимание, теперь метка перехода стоит ниже по коду в области старших адресов, потому что вверх идет область младших адресов памяти, а вниз область старших адресов. Значит, если условие удовлетворяет истинности мы входим в тело условной конструкции if и делаем безусловный переход на метку перехода Label перескакивая 18 строку. Смотрите, она не выполнится, но это только в том случае, если condition равен true. Давайте, мы пошагаем эту программу. В нашем случае condition сейчас равен false, давайте посмотрим, если у вас возникают трудности всегда нажимайте F11 и шагайте какой-то участок кода. Значит смотрим, на 11 строке мы создаем false, она и так по умолчанию false. В условной конструкции if выражение возвратит результат false. Мы не войдем в тело и перейдем сразу на 18 строку, на 21, видите? И если мы посмотрим в консоль, мы увидим, что в нашем случае вывелась и первая строка и вторая строка. Хорошо, и теперь как бы нам зайти внутрь условной конструкции? Меняем значение condition на true. Давайте шагаем. Condition = true, true == true = true. Входим? Входим. Мы попадаем на оператор безусловного перехода goto, который так не любит Дейкстра, а все его наслушались и тоже не любят. И куда мы сейчас перейдем? На 20 строку. Мы перешли на метку перехода Label, и при этом мы не выполнили 18 строку, заметили. Смотрите, в результате работы этой программы вывелась только вторая строка, значит оператор goto, как мы увидели – это оператор безусловного перехода, который может переходить, делать переходы на метки перехода как в область младших адресов, то есть в начало программы, так и в область старших адресов. Представьте, что это не строки а адреса, я вам потом покажу под рефлектором, потому покажу. Хорошо. А мы с вами переходим к рассмотрению первого настоящего цикла. Это цикл while. Давайте мы зайдем в презентацию и посмотрим как он выглядит, его еще называют циклом с предусловием. А что есть цикл с постусловием? Есть. Мы его сейчас рассмотрим. Давайте мы сразу же посмотрим на этот пример. Мы создаем переменную counter типа int и присваиваем значение 0. Counter переводится как? Счетчик. А счетчик чего? Скорее всего это счетчик итераций. Далее мы создаем циклическую конструкцию while. Обратите внимание, циклическая конструкция while состоит из трех частей. Первая часть, это имя этой циклической конструкции, имя типа этой циклической конструкции. Ключевое слово while, говорит? что мы хотим создать цикл именно while? А не какой-то другой, потому что у нас есть do-while, for, for-each, это мы дальше посмотрим. Первое ключевое слово, определяющее именно тип циклической конструкции. После него традиционно идет что? Блок условия. И если условие удовлетворяет истинности мы входим внутрь цикла, не входит, мы выходим из него. Первая часть условной конструкции – это переменная while, вторая часть условной конструкции – это условие, и третья часть условной конструкции – это ее тело. Это некий блок кода в операторных скобках. Понятно, потому что while – это ключевое слово определяющее тип условной конструкции. Вот это вместе и есть циклическая конструкция while. Значит еще раз смотрим, мы создали переменную типа int с именем counter, который будет являться счетчиком итераций нашего цикла. Создаем циклическую конструкцию while в условии которой указываем выражение counter < 3. И до тех пор пока условие будет удовлетворять истинности. Мы будем входить в тело, выполнять его и переходить на начало, что мы делаем в теле циклической конструкции? Мы увеличиваем counter на 1, и выводим значение счетчика итераций цикла на экран. Смотрим на диаграмму, как она представлена визуальным формализмом. У нас имеется условие, если условие удовлетворяет истинности, то мы переходим в тело цикла и выполняем серию команд. Иначе, если условие не удовлетворяет истинности мы выходим за пределы цикла и идем дальше по нашей программе. В код? Конечно же, идем в код.
1: using System; 2: 3: // Циклическая конструкция - while. 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: int counter = 0; 12: 13: while (counter < 3) 14: { 15: counter++; 16: Console.WriteLine("Counter {0}", counter); 17: } 18: 19: Console.WriteLine("Произведено {0} итераций.", counter); 20: 21: // Delay. 22: Console.ReadKey(); 23: } 24: } 25: }
Смотрим, на 11 строке мы создаем переменную counter типа int. Эта переменная counter будет являться счетчиком итераций цикла. Итерация – один виток, один проход по циклу. На 13 строке мы создаем циклическую конструкцию while в условии которой указываем выражение counter < 3. Если условие удовлетворяет истинности то мы входим в тело циклической конструкции while, выполняем его. Что мы здесь делаем? Увеличиваем значение счетчика итераций на 1, выводим значение счетчика итераций цикла на экран и возвращаемся снова же на условие. Только условие перестанет удовлетворять истинности мы сразу же обойдем и перейдем на следующую команду, стоящую за телом цикла. Шагаем? Конечно же. Counter присваиваем 0. На 13 строке мы смотрим в условии циклической конструкции while мы проверяем выражение counter < 3. Что он нам вернет? True. Войдем? Конечно же! Входим, увеличиваем значение счетчика на 1, выводим его на экран. Смотрите, куда мы сейчас перейдем, на 19? Нет, мы на 13 перешли. Он сам нас неким волшебным образом перевел нас сюда. Как? Скорее здесь стоит какой-то скрытый goto, а вот здесь скрытая метка, нас сюда бросает, представляете, что внутри этого цикла после последней команды стоит какая-нибудь goto, которая говорит goto, или на while. Так, counter у нас чему равен? 1. Единица меньше 3. Условие удовлетворяет истинности и мы входим в тело циклической конструкции while. Чему сейчас counter будет равен, смотрим. 2. Вывели на экран. Идем снова же. 2 меньше трех? Условие удовлетворяет истинности, входим, увеличиваем counter на 1, вывели на экран значение счетчика. Смотрите, мы в цикле вывели на экран значение счетчика итераций цикла counter. Значит counter у нас чему сейчас равен? Трем. Наводим на выражение – false. Мы войдем в тело цикла? Нет, мы не войдем в тело цикла и перешли на следующую команду за тело цикла. Вот таким простым образом работает цикл while. Цикл с предусловием. У нас есть еще цикл с постусловием. Но это мы будем дальше смотреть. Мы еще поcмотрим работу цикла while. Переходим на следующий пример.
1: using System; 2: 3: // Циклическая конструкция - while. (с досрочным выходом из цикла - break) 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: int counter = 0; 12: 13: while (counter < 3) 14: { 15: counter++; 16: Console.WriteLine("Counter {0}", counter); 17: 18: break; 19: 20: Console.WriteLine("Эта строка не выполнится."); 21: } 22: 23: Console.WriteLine("Произведено {0} итераций.", counter); 24: 25: // Delay. 26: Console.ReadKey(); 27: } 28: } 29: }
Смотрим. На 11 строке создаем счетчик итераций цикла counter. На 13 строке мы создаем циклическую конструкцию while в условии которой указываем выражение counter < 3. Если условие удовлетворяет истинности мы входим в тело цикла и увеличиваем значение счетчика и выводим его на экран. На 18 строке у нас встречается уже знакомый нам оператор break. Как переводится break? Разбивать, нарушать, ломать. Ага, а что же он должен сделать? Он должен прекратить выполнение нашего цикла и перейти на следующую команду за циклом. Давайте пошагаем, смотрите. Увеличили счетчик на 1 и он стал равен единице. Смотрите что сейчас произойдет. Мы вышли из цикла. А теперь внимание. Почему здесь используется break? Что за логика странная? А здесь нет никакой логики, мы показываем работу оператора break, что он безусловно, без всяких условий берет и прерывает выполнение цикла. Прекращает его работу. Выводит нас из цикла. И поэтому мы понимаем, что оператор break есть смысл в настоящих программах использовать только в связке с условными конструкциями. Значит еще раз, оператор break есть смысл использовать только в связке с условными конструкциями. Что это значит? Это значит, что мы должны поставить какое-то условие. Допустим если counter = 2, то тогда мы выходим их цикла. Иначе мы продолжаем его выполнение. Вот это и есть смысл фразы, что оператор break есть смысл использовать только в связке с условными конструкциями. Хорошо, а мы переходим к следующему примеру. Давайте посмотрим.
1: using System; 2: 3: // Циклическая конструкция - while. (с пропуском итерации - continue) 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: int counter = 0; 12: 13: while (counter < 3) 14: { 15: counter++; 16: Console.WriteLine("Counter {0}", counter); 17: 18: continue; 19: 20: Console.WriteLine("Эта строка не выполнится."); 21: } 22: 23: Console.WriteLine("Произведено {0} итераций.", counter); 24: 25: // Delay. 26: Console.ReadKey(); 27: } 28: } 29: }
На 11 строке мы создаем счетчик итераций цикла count. На 13 строке мы создаем циклическую конструкцию while в условии которой указываем выражение counter < 3. В теле цикла while мы увеличиваем значение счетчика итераций на 1. Выводим значение счетчика на экран. Обратите внимание, на 18 строке у нас встречается оператор continue. Интересно как он переводится? Он переводится – продолжить. Интересно что он сделает. Если break нас выбрасывал из цикла, ток что же делает continue. Смотрите, здесь подчеркнуто зеленым, это warning. А что это за warning? Давайте посмотрим, одно предупреждение у нас имеется. То есть мы понимаем, что этот код никогда не выполнится. Представляете, мы сюда никогда не зайдем. Почему? Давайте пошагаем. Так, continue, что же он нам сделает? Обратите внимание, что делает оператор continue? Он возвращает нас на начало цикла. Он возвращает нас на условие. Запомните, continue возвращает нас, переводит выполнение программы на условие. И получается, что как бы мы не выполняли эту программу, мы никогда не перешли на 20 строку. Вы скажете и это, как и предыдущая иррациональная программа. Да. Потому что задача этой программы, показать работу оператора continue. Мы с вами понимаем, что, как и оператор break, оператор continue есть смысл использовать только в связке с условной конструкцией. Например, если counter = 2, мы перейдем… Или какие-то другие условия поставить. Все зависит от логики нашей программы. Но мы сейчас смотрим только работу. Работу простейших конструкций. Хорошо. Мы переходим дальше. Смотрим пример Предположение цвета.
1: using System; 2: 3: // Циклическая конструкция - while. 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: Console.WriteLine("Угадайте задуманный цвет с пяти попыток."); 12: Console.WriteLine("Для выхода из программы введите - exit."); 13: 14: const int maxAttempt = 5; // Допустимое количество попыток. 15: int attempt = 0; // Счетчик попыток. 16: string color = "red"; // Задуманный цвет. 17: 18: while (attempt < maxAttempt) 19: { 20: attempt++; 21: Console.WriteLine("Попытка {0}:", attempt); 22: 23: string value = Console.ReadLine(); 24: 25: if (value == "exit") 26: { 27: break; 28: } 29: 30: if (value != color) 31: { 32: continue; 33: } 34: 35: Console.WriteLine("Поздравляем, Вы угадали с {0} попытки!", attempt); 36: break; 37: } 38: 39: Console.WriteLine("Конец игры!"); 40: 41: // Delay. 42: Console.ReadKey(); 43: } 44: } 45: }
Здесь нам предлагается угадать задуманный цвет с 5 попыток. На 11 строке мы предлагаем пользователю угадать задуманный цвет с пяти попыток. Также мы его обнотифаеваем, что если вдруг ему надоест угадывать задуманный цвет, то для выхода из программы он может ввести слово exit и выведется. И на 14 строке мы создаем константу типа int с именем maxAttempt и присваиваем значение 5. Это допустимое количество попыток. На 15 строке мы устанавливаем счетчик попыток, на 16 строке мы устанавливаем задуманный цвет, в данном случае это red. Далее, на 18 строке мы создаем циклическую конструкцию while, в условии которой указываем выражение. Attempt, то есть счетчик попыток должен быть меньше допустимого количества попыток, потому что мы даем только 5 раз возможность угадать задуманный цвет. Потом – вы проиграли. На 20 строке. Если все-таки мы не исчерпали количество попыток, то мы входим в тело циклической конструкции и на 20 строке счетчик попыток увеличивается на единицу. Попытки уменьшаются. На 21 строке мы выводим пользователю сообщение. Говорим, что это попытка номер один. Далее на 23 строке мы строковой переменной value присваиваем значение цвета введенное пользователем с клавиатуры. Далее мы проверяем, вдруг ему с первой попытки надоело вводить, угадывать цвета. Он вводит exit. И ему что должно? Мы должны выйти. Давайте проверим. Точно ли так сработает? Угадайте задуманный цвет с пяти попыток. Для выхода из программы введите exit. Попытка №1. Я пытаюсь угадать цвет, допустим green. Нет я хочу выйти. Мы берем и выходим. Exit. Конец игры! Все, здесь у нас сработало. Так тестируем, тестируем. Мы вышли за пределы цикла. Break вывел нас за пределы цикла на 39 строку и мы увидели сообщение, которое вот Конец игры! Но если я введу какой-то цвет. Я какой пытался ввести? Зеленый, представьте, что я ввел зеленый цвет. Value будет равно green. Зеленый не равен exit – не выходим. На 30 строке в условной конструкции if у нас есть выражение зеленый не равен красному – истина. И мы входим в тело условной конструкции if. И здесь выполняем оператор continue, который что нам сделает? Он переводит нас на начало цикла. Давайте пошагаем. Здесь у нас проверка 0 меньше 5, все нас пускает. Смотрите, первая попытка нам выведется сейчас. У меня первая попытка. Мне предлагается ввести. Я ввожу сейчас green – зеленый цвет. Нажимаю Enter. Смотрите, меня перебрасывает вот куда. Смотрим, green равен exit – false. Мы не войдем сюда. Переходим сюда. Смотрим, value – green, color – red. Равно? Не равно! Удовлетворяет истинности? Заходим в тело условной конструкции. Зеленый не равен красному – это истина. Высказывание истинное. Переходим на оператор continue. Куда нас? На while обратно. Нас перебросил обратно на while. Нажимаем теперь F5. Чтобы нам нормально выполнить эту программу. Попытка 2. Попробуем ввести yellow, нет. Третья попытка. И теперь я ввожу попытка red. Все, поздравляем, вы угадали с 3 попытки. Конец игры. Обратите внимание, что если я угадываю цвет. Давайте мы отшагаем с возможностью угадывания цвета. Смотрите, для этого я поставлю вот здесь где 30 строка и вот эта серая полосочка поставлю точку останова, или еще ее называют breakpoint. Как ее поставить? Просто наведите сюда мышку и кликните левой кнопкой мыши один раз. Появилась красная точечка. Чтобы мне через F11 не шагать все время мы остановимся здесь. Нажмите F5. Сейчас нажался у нас F5. Я ввожу красный цвет. Смотрите, угадываю с первой попытки. Смотрите, наша программа остановилась. Видите, меня еще никто не поздравил. Меня еще не поздравил, что я угадал цвет. Значит наша программа остановилась на 30 строке. Если я нажму сейчас F5, она выполнится вся до конца. Но я дальше хочу шагать. Вот здесь я хочу, чтобы она выполнилась быстро, а вот здесь чтобы через F11 шагать. Здесь я нажал F5. Видите, какая ловушка для программы. Остановились. И дальше я начинаю шагать. Смотрим, value – red, color – red. Красный не равен красному. Это false. Мы не войдем сюда, мы не перейдем на начало цикла. Давайте посмотрим, я нажимаю F11. Смотрите, мы перешли на поздравление. Меня сейчас будет поздравлять. Дальше, меня уже поздравили, и что делать? Если мы не укажем здесь break, то что произойдет? Меня будут опять заставлять отгадывать цвета. А я не хочу этого. Я уже угадал цвет. Причем с первой попытки. Я хочу приз. Замечательно. Мы нажимаем F11, нас выводит на конец игры. Обратите внимание, вот это интересная комбинация работы операторов break и continue. Мы понимаем, что операторы break и continue чаще всего есть смысл использовать только лишь в связке с условными конструкциями. Но иногда они могут работать и сами по себе. Все зависит от логики работы нашей программы. Мы с вами разобрали работу циклической конструкции while. Как переводится while? Пока или до тех пор. Это цикл с предусловием.
Следующий цикл do-while. Давайте посмотрим, цикл с постусловием. Смотрите, как выглядят его ключевые слова, из которых он состоит. Do – делать, while – до тех пор. Давайте посмотрим, мы создаем счетчик итераций цикла. Далее мы указываем ключевое слово do – делать. Смотрите, нет никакой проверки, и поэтому процессор, который буде читать эту программу, выполнять эту программу. Он безусловно войдет в этот цикл. Вообще его никто здесь не ждет, никаких условий, никто ему не запрещает и не проверяет. Он безусловно заходит в тело, но скажу вам, это первый раз он только так зайдет. Дальше его встретит условие. Значит еще раз, мы создали счетчик итераций цикла, встречаем слово do, заходим в тело цикла, увеличиваем счетчик на 1, выводим значение счетчика итераций цикла на экран и далее нас встречает условие – counter < 3. И если условие удовлетворяет истинности, то мы переходим снова же на do, входим в тело цикла и выполняем его. Если же условие не удовлетворяет истинности то мы больше войдем в этот цикл, а выйдем из него. Обратите внимание, основное отличие. Цикл while встречает на сразу условием, сразу нас проверяет, а цикл do-while позволяет выполнить свое тело безусловно. У него идет постусловие. У одного пре условие, а у другого пост условие. Смотрите, это даже еще проще. Безусловно зашли, выполнили, проверили… Давайте посмотрим, как выглядит этот цикл в визуальном формализме. Вот мы читаем его, безусловно выполняем серию команд. Далее проверяем, если условие удовлетворяет истинности. То мы переходим на выполнение этой серии команд. Если же условие не удовлетворяет истинности, мы больше никогда не войдем в это тело, выходим и выполняем программу дальше. Еще проще чем цикл while. Давайте мы теперь перейдем в программный код. Зайдем в Visual Studio и посмотрим как же работает этот цикл в действительности.
1: using System; 2: 3: // Циклическая конструкция - do-while. 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: int counter = 0; 12: 13: do 14: { 15: counter++; 16: Console.WriteLine("Counter {0}", counter); 17: } 18: while (counter < 3); 19: 20: Console.WriteLine("Произведено {0} итераций.", counter); 21: 22: // Delay. 23: Console.ReadKey(); 24: } 25: } 26: }
На 11 строке мы создаем счетчик итераций цикла с именем counter. На 13 строке мы создаем циклическую конструкцию do-while, в теле которой мы увеличиваем значение счетчика на 1. На 16 строке мы выводим на экран значение счетчика итераций, и на 18 строке мы проверяем условия counter < 3, и если условие удовлетворяет истинности мы переходим на начало цикла и выполняем тело заново. Если же условие не удовлетворяет истинности, то мы больше не войдем сюда, мы просто идем дальше по ходу выполнения программы. Что? Шагаем? Конечно же шагаем. Какую клавишу я нажал? F11. Так создали счетчик итераций. Видите, мы безусловно вошли в тело, увеличиваем счетчик на 1, выводим на экран значение, я его вывел. И теперь мы переходим на условие. Если условие удовлетворяет истинности мы пойдем снова же наверх, если удовлетворяет, если не удовлетворяло – вышли бы. Смотрите, пошли снова. Удовлетворяет истинности? Counter = 2. 2 < 3. Условие удовлетворяет истинности. Давайте закрепим подсказку, чтобы нам подсказывало постоянно. Counter менше 3 – false. Видите, загорелся красным. Почему? Потому что counter = 3, а три не меньше трех. Перейдем обратно или нет. Давайте посмотрим, мы вышли за пределы цикла. Вот таким образом работает цикл do-while. То есть циклическая конструкция с постусловием. Еще проще чем работа обычного цикла while. Смотрим следующий пример.
1: using System; 2: 3: // Циклическая конструкция - do-while. (с досрочным выходом из цикла - break) 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: int counter = 0; 12: 13: do 14: { 15: counter++; 16: Console.WriteLine("Counter {0}", counter); 17: 18: break; 19: 20: Console.WriteLine("Эта строка не выполнится."); 21: } 22: while (counter < 3) ; 23: 24: Console.WriteLine("Произведено {0} итераций.", counter); 25: 26: // Delay. 27: Console.ReadKey(); 28: } 29: } 30: }
На 11 строке мы создаем переменную счетчика итераций цикла counter. На 13 строке мы создаем циклическую конструкцию do while. Она заканчивается где? На 22 строке. Это что значит? Что сначала войдет в тело, а затем проверит условие. Мы безусловно первый раз зайдем в тело этого цикла. На 15 строке мы увеличиваем counter на 1. Шагаем? Давайте! Увеличиваем counter на 1. Смотрите, мы попали на break, что сейчас произойдет? Что? Мы перейдем на начало? Выйдем? Ну конечно же выйдем, потому что break у нас переводится – ломать, прекращать. И мы понимаем, что оператор break чаще всего есть смысл использовать в связке с условной конструкцией, чтобы мы не просто выходили, а то в данном случае программа кажется на иррациональной. Настоящий программист спросит, а зачем вы написали эту строку, она же никогда не выполнится. Это лишнее. Это как фраза по-русски: «Он покачал своей головой». Так он не может покачать чужой головой. Поэтому это лишняя строка, как и слово «своей». Так, выполняемся дальше. Смотрите, оператор break прекратил выполнение цикла do-while. Давайте посмотрим следующий пример.
1: using System; 2: 3: // Циклическая конструкция - do-while. (с пропуском итерации - continue) 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: int counter = 0; 12: 13: do 14: { 15: counter++; 16: Console.WriteLine("Counter {0}", counter); 17: 18: continue; 19: 20: Console.WriteLine("Эта строка не выполнится."); 21: } 22: while (counter < 3); 23: 24: Console.WriteLine("Произведено {0} итераций.", counter); 25: 26: // Delay. 27: Console.ReadKey(); 28: } 29: } 30: }
На 11 строке мы создаем счетчик итераций цикла counter с 13 по 22 строку м ы создаем циклическую конструкцию с постусловием do-while в теле которой мы на 15 строке увеличиваем counter на 1. На 16 строке мы выводим значение счетчика итераций цикла на экран. И тут нам опять встречается этот оператор continue. Что же он делает? Тоже нас выводит из цикла? Нет? Помните я говорил, не на начало цикла а куда? На условие. Давайте шагать. Он переводит нас на условие. Так, увеличили счетчик на 1, вывели на экран. Смотрите, на 22 строке на условии, куда мы пойдем дальше? Снова выполняемся. Смотрите, 20 строка опять не выполняется, опять иррациональная программа. Но мы понимаем, что такая простая программа и такое использование оператора continue призвано показать просто его чистую работу, и мы понимаем, что в большинстве случаем оператор continue нам есть смысл использовать в связке с условными конструкциями. И мы видим, что 20 строка никогда не выполнится. Потому что мы сюда никогда не перейдем, здесь происходит безусловный переход, похоже на метку goto на 22 строку. Хорошо. Мы переходим дальше. Так, факториал. Но не пугайтесь – это просто. Факториал числа, возможно вы слышали. Если вы не слышали мы сейчас посмотрим что это такое. Смотрите, как записывается факториал. N – это какое-то число и восклицательный знак – обозначает факториал числа. Как рассчитывается факториал? Если у нас факториал 4, то мы берем 1 умножаем на 2*3*4. То есть мы берем натуральный ряд тех чисел, факториал которого мы хотим вычислить. Если 4, то натуральный ряд от одного до 4. 1,2,3,4 и умножаем их друг на друга. Все! А зачем? Эта самая важная математическая операция в комбинаторике. Представьте себе ситуацию. Нет, мы не IT компания, мы – интернет магазин. И мы торгуем книгами. Я прихожу на работу, открываю свою CRM систему и вижу, что у меня с сайта заказали 4 книги, и мне их нужно развести по разным частям города, по разным станциям метро, которые расположены в разных сторонах света от меня. Так, четыре человека заказали книги, что же мне делать. Мне нужно посчитать оптимальный маршрут. Но не переживайте, мы не будем считать транспортных задач, не будем использовать симплекс методы. Мы просто посчитаем, а сколько вообще вариаций доставки четырех книг клиентам у нас имеется. Сколько возможных маршрутов мне можно выдумать. Представьте, что мы доставляем товары по разным городам. Например, мы живем в Санкт-Петербурге и один товар мне нужно доставить в Киев, другой мне нужно доставить в Москву, третий во Владивосток, а четвертый – в Лондон. Мы находимся в Санкт-Петербурге. Заказчики: Киев, Москва, Владивосток, Лондон. Замечательно, сначала мы едем во Владивосток. Потом мы едем в Лондон, потом мы едем в Москву, потом в Киев. Но Москва же по пути во Владивосток. Киев по пути к Лондону. Зачем ехать в Лондон, потом во Владивосток, а потом возвращаться в Киев. Если у меня 4 города, тогда у меня всего сколько вариантов доставки? А вот – 24. Смотрите, факториал 4 рассчитывается вот так: 1*2*3*4 = 24 возможный маршрута, как я пошлю свою машину. Представьте, у меня только одна машина – Скания, какая-нибудь такая модная. И поэтому у меня имеется только 24 способа доставки этого товара. И теперь мне нужно посчитать эти отрезки. Выбрать самый короткий путь. Но это уже не задачи факториала. Факториал мне должен просто сказать сколько имеется вариантов доставки. А мы переходим к программному коду, чтобы понять, как мы можем реализовать на языке программирования, с использованием нашей циклической конструкции сделать расчет факториала. Здесь важно еще запомнить, что факториал 0 равен 1.
1: using System; 2: 3: // Циклическая конструкция - do-while. 4: 5: // Факториал числа n (обозначается n!, произносится - эн факториал) — это произведение всех натуральных чисел, 6: // от 1 до n включительно: 7: // n! = 1 * 2 * 3 * ... * n 8: // 0! = 1 9: 10: // В комбинаторике факториал натурального числа n интерпретируется как количество перестановок множества из n элементов. 11: // Например, для множества {A,B,C,D} из 4-х элементов существует: 4! = 1 * 2 * 3 * 4 = 24 перестановки: 12: 13: // ABCD BACD CABD DABC 14: // ABDC BADC CADB DACB 15: // ACBD BCAD CBAD DBAC 16: // ACDB BCDA CBDA DBCA 17: // ADBC BDAC CDAB DCAB 18: // ADCB BDCA CDBA DCBA 19: 20: namespace Loop 21: { 22: class Program 23: { 24: static void Main() 25: { 26: int counter = 4; 27: int factorial = 1; 28: 29: Console.Write("Факториал числа: {0}! = ", counter); 30: 31: do 32: { 33: // Сначала умножение, потом декремент. 34: factorial *= counter--; 35: 36: // Данная строка эквивалентна: 37: 38: //factorial = factorial * counter; 39: //counter = counter - 1; 40: } 41: while (counter > 0); 42: 43: Console.WriteLine("{0}", factorial); 44: 45: // Delay. 46: Console.ReadKey(); 47: } 48: } 49: }
На 26 строке мы создаем счетчик итераций цикла counter = 4. На 27 строке мы создаем переменную factorial и присваиваем ей 1. На 29 строке мы выводим на экран факториал этого числа. Смотрите, write – без перевода строки. Он останется еще ждать. И далее нас встречает циклическая конструкция do-while. Первый раз мы заходим как? Безусловно. И мы переменной factorial на первой безусловной итерации цикла присваиваем с умножением factorial умноженный на counter а только потом мы выполняем декремент counter. Тоесть мы сначала берем 4, умножаем на 1 и присваиваем factorial 4. Держим четверку в уме, загибаем пальчики, на 24 нам не хватит ну ничего, будем в уме держать. Так, factorial присвоил 4, counter стал 3. Counter больше нуля? Больше. Идем дальше. Factorial чему равен? 4. Counter чему равен? 3. 4*3 = 12. Factorial равен 12. Counter уменьшаем на 1 = 2. 2 больше 0? Больше! Идем сюда. 12 умножаем на 2 = 24. Factorial = 24. Уменьшаем counter на 1 = 1. 1 больше 0? Больше! Идем на начало. 24 * 1 = 24. Counter -1 = 0. Counter больше нуля – false. Условие перестало удовлетворять истинности и далее мы выводим сюда без перевода строки. 24. Давайте выполнимся. Видите? Факториал числа 4 равен 24. Просто? Просто. Факториал – это очень простая операция, которая очень часто используется в начале комбинаторики. Замечательно. Теперь мы уже посмотрели сколько циклических конструкций? Пока две, но одну не совсем настоящую мы рассмотрели – оператор безусловного перехода goto, далее мы рассмотрели циклическую конструкцию do-while с предусловием. Помним, что здесь еще встречаются такие операторы как break и continue? Которые ест смысл использовать чаще всего в связке с условными конструкциями. Цикл с постусловием – do-while. Сначала выполняем тело первый раз. И только потом проверяем условия. Здесь мы можем использовать break и continue? Конечно же можем. И тоже желательно их использовать в связке с условными конструкциями. А теперь мы переходим к последнему циклу, который мы рассмотрим на этом занятии – это цикл for.
Это самый простой цикл, еще проще чем те, которые мы с вами рассмотрели. Внимание, а чем же он проще? Обратите внимание, что сделал это цикл? Он вобрал в себя сразу же и счетчик итераций цикла и условия, и изменение счетчика итерации. Если раньше это все было разбросано. Помните цикл while. Счетчик здесь, изменение его внутри, проверка здесь. У do-while тоже видите счетчик, изменение внутри, проверка вообще вот здесь. То цикл for, его и называют цикл со счетчиком. Смотрите, мы создаем циклическую конструкцию for где указываем. Сразу же создаем счетчик, видите мы создаем счетчик итераций цикла counter типа int и присваиваем значение 0. Ставим точку с запятой. Далее идет условие. Counter<3, если условие удовлетворяет истинности – мы входим в тело цикла. Если же не удовлетворяет, мы выходим за пределы цикла. Обратите внимание, корректировка счетчика, изменение счетчика. Три позиции: создание счетчика, проверка условия и изменения счетчика. Мы взяли вот отсюда поставили сюда счетчик, отсюда его изменение, условие осталось на месте. Условие посередине. Видите, как просто. Значит мы создаем циклическую конструкцию for со счетчиком, создаем счетчик итераций цикла типа int и присваиваем 0, точка с запятой, сразу же ставим условие counter < 3, точка с запятой и изменяем счетчик. Обратите внимание, как выполняется этот цикл. Сначала создается счетчик, проверяется условие, если условие удовлетворяет истинности мы идем в тело, выполняем тело и сразу в правый верхний угол на изменение счетчика. Изменяем счетчик на условие. Влево, вниз, вправо-вверх, влево, вниз, вправо-вверх… И только если условие перестанет удовлетворять истинности, то мы пойдем и выйдем за пределы этого цикла. Снова же смотрим теперь на визуальный формализм представления циклической конструкции for со счетчиком на диаграмме. Смотрите, у нас создается счетчик, далее, идем сразу же на условие. Проверили, если условие удовлетворяет истинности, входим в тело цикла и выполняем серию команд. Если же счетчик, не удовлетворяет истинности, если условие не удовлетворяет истинности, то мы выходим за пределы цикла и выполняем далее нашу программу. Еще раз, создание счетчика, проверка условия, выполнение серии команд, если условие удовлетворяет истинности, изменяем счетчик, на условие, серия команд, изменение счетчика, на условие. Вот такой треугольник запомните. Вот так мы ходим по этому циклу. Просто? Просто. Смотрите, какой чистый и простой цикл. Все, что было раньше разбросано, теперь собрали в одну строку, это наиболее часто используемый цикл программистами. Самая популярная циклическая конструкция for – цикл со счетчиком. И давайте посмотрим, как он работает в коде. Зайдем в Visual Studio. Заходим в 12 проект и смотрим.
1: using System; 2: 3: // Циклическая конструкция - for (цикл со счетчиком). 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: 12: // for ( Инициализация счетчика итераций; Условие; Изменение счетчика ) { Тело цикла } 13: 14: for (int counter = 0; counter < 3; counter++) 15: { 16: Console.WriteLine("Counter = {0}", counter); 17: } 18: 19: //counter = 0; // counter - недоступен за пределами цикла for. 20: 21: // Delay. 22: Console.ReadKey(); 23: } 24: } 25: }
На 14 строке мы создаем циклическую конструкцию for. Создаем счетчик итераций цикла и сразу же запятую после него. Указываем условие counter < 3. Если условие удовлетворяет истинности, то мы входим в тело и выполняем серию команд, иначе – выходим. Мы входим и выполняем серию команд, переходим на изменение счетчика, в данном случае счетчик увеличивается на 1, в каких-то случаях мы можем его уменьшать. В каких-то случаях это может проходить по-другому. Что? Шагаем? Хорошо! Давайте шагаем. Создаем счетчик итераций, проверяем условие, входим в тело цикла. Дальше идем куда? Видите, треугольник, бермудский треугольник: вниз, вправо-вверх, влево. Идем на условие после увеличение счетчика. Он стал равен 1. Куда идем? В тело цикла. Потом на изменение счетчика. Счетчик = 2, входим. Счетчик чему равен сейчас? 3. 3 меньше 3. Куда? Выйдем. Конечно же выйдем из цикла. Обратите внимание, еще маленькая особенность. Но в данном случае, счетчик итераций не будет доступен за пределами цикла for. Если мы снимем комментарий с 19 строки Ctrl+I,U. Смотрите, Подчеркивается красным. Что у нас тут пишет? Ага, counter doesn’t exist in the current context. Он не видит, что находится в другой локальной области. В в JavaScript можно так сделать? Конечно же! Там так сделать можно. В C# так сделать нельзя. Хорошо, а мы переходим к следующему примеру.
1: using System; 2: 3: // Циклическая конструкция (цикл со счетчиком) - for (с досрочным выходом из цикла - break). 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: 12: for (int counter = 0; counter < 3; counter++) 13: { 14: Console.WriteLine("Counter = {0}", counter); 15: 16: break; 17: 18: Console.WriteLine("Эта строка не выполнится."); 19: } 20: 21: // Delay. 22: Console.ReadKey(); 23: } 24: } 25: }
На 12 строке мы создаем циклическую конструкцию for со счетчиком. Создаем счетчик итераций counter, указываем условие counter < 3. И указываем изменение этого счетчика. Конечно же мы зайдем в тело цикла for. Смотрите, и здесь снова нас встречает оператор break. Все он нас никак не может оставить. Мы его здесь специально показываем, чтобы вы запомнили его. Вы уже видите, что оператор break может использоваться во всех циклических конструкциях, чтобы прервать их работу в нужный момент. И мы понимаем, что оператор break в данном случае не совсем логично используется, как всегда. Его есть смысл чаще всего использовать в связке с условными конструкциями. Мы переходи на следующий пример.
1: using System; 2: 3: // Циклическая конструкция (цикл со счетчиком) - for (с пропуском итерации - continue). 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: 12: for (int counter = 0; counter < 3; counter++) 13: { 14: Console.WriteLine("Counter = {0}", counter); 15: 16: continue; 17: 18: Console.WriteLine("Эта строка не выполнится."); 19: } 20: 21: // Delay. 22: Console.ReadKey(); 23: } 24: } 25: }
Снова же создаем цикл for, создаем счетчик итераций цикла counter, условие изменения счетчика. Если мы войдем в тело цикла, нас встречает что? Continue. А что делает continue? Он переводит нас сна условие. Давайте пошагаем, и увидим правда ли это. Он перевел нас на увеличение счетчика, потом на условие и т.д. На 18 строке, строка никогда не выполнится, потому что программа немного алогична, потому что оператор continue есть смысл использовать также в связке с условными конструкциями. И мы уже видели, что оператор continue может использоваться во всех трех циклических конструкциях. Напомните пожалуйста я забыл. While, do-while и цикл for. Давайте посмотрим теперь на вложенные циклы. Вложенный цикл for.
1: using System; 2: 3: // Цикл for вложенный в цикл for. ( Построение квадрата из звездочек - * ) 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: 12: for (int i = 0; i < 10; i++) 13: { 14: // Выводим одну строку из 10 звездочек. 15: for (int j = 0; j < 10; j++) 16: { 17: Console.Write("*"); 18: } 19: 20: // Переход на новую строку. 21: Console.WriteLine(); 22: } 23: 24: // Delay. 25: Console.ReadKey(); 26: } 27: } 28: }
Обратите внимание, на 12 строке мы создаем цикл for размерностью в 10 итераций. Вот этот внешний цикл for выполнится 10 раз. И смотрим на 15 строке у нас имеется воженный цикл for. Вот он вложенный цикл for. Я нажимаю Shift+Alt+DownArrow чтобы красиво выделить. Видите, вот вложенный цикл for. Это получается, что на первой итерации внешнего цикла, вот когда он зайдет на первую итерацию, то вложенный цикл выполнится 10 раз. По 10 итераций вложенного цикла на одну итерацию внешнего. И получается, что вложенный цикл сам по себе имеет 10 итераций, но вот эти 10 итераций будут выполнены 10 раз по 10, потому что здесь 10 внешних итераций. Давайте мы пошагаем, посмотрим. Давайте мы здесь поставим тройку, чтобы нам быстрее отшагать. Теперь берем что? F11. Смотрите заходим и выполняем раз, два, три. Смотрите, мы вывели три звездочки в строку, давайте посмотрим, видите, три звездочки вывели. В ряд. Write не переводит строку, WriteLine переводит. Значит получается, что делает наш вложенный цикл for? Он выводит строку из звездочек. Давайте дальше. Так увеличили, вышли. Что мы сделали. Видите, где курсор сейчас мигает? Мы сейчас переведем его на новую строку. Смотрите, вы видите где курсор? Мы перевели курсор на новую строку, благодаря выполнению вот этого метода WriteLine. Куда мы сейчас пойдем? Ну конечно же на начало внешнего цикла for. Еще раз три раза выполняем внутренний цикл. Что он нам сделал? Вывел еще одну строку. Еще одна строка вывелась из трех звездочек. Потом идем сюда. Что мы сделаем? Переводим курсор на новую строку. Видите, где сейчас курсор? После перевода, смотрите, видите где он? Еще раз заходим. Что у нас здесь делается? Выводится строка из трех звездочек. Шагаем дальше. Переводим. Смотрим, мы нарисовали такой псевдоквадрат из звездочек. Просто? Просто. Возвращаем наши значения, и потому если мы выполним что здесь будет? Что сделает эта программа? Она вернет квадрат размерностью 10 на 10. Давайте выполнимся и посмотрим. А если мне надо нарисовать треугольник? Что же мне здесь делать? Нужно изменить одно число. Нужно во вложенном цикле изменить условие на j
1: using System; 2: 3: // Цикл Дейкстры. 4: 5: // При выполнении цикла Дейкстры в каждой итерации происходит вычисление охраняемых условий. 6: // Если хотя бы одно из них истинно, выполняется соответствующая охраняемая команда, после чего начинается новая итерация 7: // (если истинны несколько охраняемых условий, выполняется только одна охраняемая команда). 8: // Если все охраняемые условия ложны, цикл завершается. 9: // Цикл Дейкстры с одним охраняющим условием и одной охраняемой командой представляет собой, по сути, обычный цикл с предусловием (цикл «while»). 10: 11: // Классическое описание цикла Дейкстры. 12: // do 13: // P1 → S1, 14: // … 15: // Pn → Sn 16: // od 17: 18: // где: 19: // do — маркер начала конструкции цикла, 20: // od — маркер завершения конструкции цикла, 21: // Pi — i-тое охраняющее условие (логическое выражение, которое может иметь значение «истинно» или «ложно»), 22: // Si — i-я охраняемая команда. 23: 24: // Цикл состоит из одной или нескольких ветвей (охраняемых выражений), 25: // каждая из которых представляет собой пару из охраняющего условия и охраняемой команды. 26: 27: namespace Loop 28: { 29: class Program 30: { 31: static void Main() 32: { 33: char character = '\0'; 34: 35: for (; ; ) 36: { 37: 38: character = Console.ReadKey().KeyChar; 39: 40: switch (character) 41: { 42: case 'l': // 'l' - охраняющее условие 1. 43: { 44: Console.WriteLine("Go left"); // охраняемая команда. 45: continue; 46: } 47: case 'r': // 'r' - охраняющее условие 2. 48: { 49: Console.WriteLine("Go right"); // охраняемая команда. 50: continue; 51: } 52: } 53: break; // Прерывание цикла. 54: } 55: 56: // Delay. 57: // Console.ReadKey(); 58: } 59: } 60: }
Обратите внимание, на 33 строке мы создаем символьную переменную character. На 35 строке мы создаем циклическую конструкцию for. Обратите внимание, она бесконечна. У нее нет условия, и поэтому если мы не организуем правильную логику внутри этой конструкции, правильную логику выхода. Мы уже видим break. Если мы не организуем правильную логику выхода, то у нас будет какой цикл? Бесконечный, как в первом примере. Значит здесь предлагается пользователю ввести значение. Пользователь вводит какое-то значение из клавиатуры. На 40 строке мы видим у нас имеется переключатель swich, который в качестве выражения селектора принимает значение введенное пользователем. В swich имеется два case, с двумя постоянными выражениями. L и R. И если выражение селектора совпадет с l, то у нас соответствующий блок выполнится. Дальше что произойдет? Переход на начало цикла. Если r – снова же на начало цикла, и до тех пор пока мы будем вводить l и r у нас будет выполнятся этот цикл. Если мы введем что-то отличное от l и r, что произойдет? У нас значение выражения селектора не совпадет ни с одним из постоянных выражений операторов caseб мы выйдем из конструкции swich и здесь нас встречает break. Прерывание цикла. И break позволит нам выйти из цикла. А теперь смотрим, на 42 строке это постоянное выражение в нотации Дейкстры называется охраняющим условием 1. Значение постоянного выражения второго case является охраняющим условием 2. А то что находится внутри – охраняемые команды, которые охраняются вот этими условиями. Если мы введем что-то отличное, мы никогда не попадем в эту команду. Никогда мы ее не выполним. Вот идет такая организация циклов. Мы понимаем, что здесь мы можем использовать и другой цикл. Например, цикл while. Не обязательно for. Как это будет выглядеть. Ну это можно сделать очень просто. Написать while, а вместо условия безусловно написать true. Получится по сути то же самое. Вот это вот такая примерная простейшая конструкция с охраняющими условиями охраняемых команд. Охраняющий условия – это представьте, что охранная собака сидит. А вот это тот склад, который она охраняет. И теперь то же самое давайте посмотрим в комментариях. При выполнении цикла Дейкстры в каждой итерации происходит вычисление охраняемых условий. Вычисление охраняемых условий. Если хотя бы одно из них истинно, выполняется соответствующая охраняемая команда, после чего начинается новая итерация (если истинны несколько охраняемых условий, выполняется только одна охраняемая команда). Если все охраняемые условия ложны, цикл завершается. То есть мы видим что-то отличное от l и r, то наш цикл просто завершится. Цикл Дейкстры с одним охраняющим условием и одной охраняемой командой представляет собой, по сути, обычный цикл с предусловием (цикл «while»). Вот идет классическое описание цикла Дейкстры. Это вот просто такое формальное описание.
Классическое описание цикла Дейкстры. do P1 → S1, … Pn → Sn od где: do — маркер начала конструкции цикла, od — маркер завершения конструкции цикла, Pi — i-тое охраняющее условие (логическое выражение, которое может иметь значение «истинно» или «ложно»), Si — i-я охраняемая команда.
Цикл состоит из одной или нескольких ветвей (охраняемых выражений), каждая из которых представляет собой пару из охраняющего условия и охраняемой команды. И когда вы будете читать об этом цикле либо в литературе, либо на Википедии. То вам уже будет проще. Вы можете зайти в пример. Вот этот формализм описания тоже. Все не сложно. А мы идем дальше. Закроем этот пример и переходи к циклу Паук.
1: using System; 2: 3: // Цикл Паук (Spider). Модифицированный цикл Дейкстры с явными условиями выхода. 4: 5: // Цикл Дейкстры не содержит явного условия продолжения или выхода. 6: // Поэтому была предложена усложнённая конструкция цикла Дейкстры, получившая название "цикл-паук". 7: 8: // В нотации Дейкстры она выглядит следующим образом: 9: 10: // do 11: // P1→S1, 12: // … 13: // Pn→Sn 14: // out 15: // Q1→T1, 16: // … 17: // Qn→Tn 18: // else 19: // E 20: // od 21: 22: // где: 23: // do — маркер начала конструкции цикла, 24: // od — маркер завершения конструкции цикла, 25: // Pi — i-тое охраняющее условие (логическое выражение, которое может иметь значение «истинно» или «ложно»), 26: // Si — i-я охраняемая команда. 27: // После маркера out добавлены ветви завершения, состоящие из условий выхода Qi и команд завершения Ti. 28: // Кроме того, добавлена ветвь альтернативного завершения else с командой E. 29: 30: // Цикл-'паук' выполняется так: 31: // Вычисляются охраняющие условия. 32: // Если существует истинное охраняющее условие, выполняется соответствующая охраняемая команда. 33: // Вычисляются условия выхода. 34: // Если существует истинное условие выхода, выполняется соответствующая команда завершения, 35: // после чего выполнение цикла заканчивается. 36: // Если все условия выхода ложны, начинается следующая итерация, но только в том случае, если в текущей итерации 37: // было истинным хотя бы одно из охраняющих условий. 38: // Если в данной итерации оказались ложными и все охраняющие условия, и все условия выхода, выполняется 39: // команда альтернативного завершения E, после чего выполнение цикла прерывается. 40: 41: namespace Loop 42: { 43: class Program 44: { 45: static void Main() 46: { 47: char character = '\0'; 48: 49: bool flag = default(bool); 50: 51: for (; ; ) 52: { 53: Start: 54: flag = false; 55: character = Console.ReadKey().KeyChar; 56: 57: switch (character) 58: { 59: case 'l': // 'l' - охраняющее условие 1. 60: { 61: Console.WriteLine("Go left"); flag = true; break; // охраняемая команда. 62: } 63: case 'r': // 'r' - охраняющее условие 2. 64: { 65: Console.WriteLine("Go right"); flag = true; break; // охраняемая команда. 66: } 67: } 68: 69: switch (character) 70: { 71: case 'x': // 'x' - условие выхода 1. 72: { 73: Console.WriteLine("Exit"); goto End; // команда завершения. 74: } 75: case 'q': // 'q' - условие выхода 2. 76: { 77: Console.WriteLine("Quit"); goto End; // команда завершения. 78: } 79: } 80: 81: // Ветвь альтернативного завершения. 82: if (flag) 83: { 84: goto Start; 85: } 86: 87: Console.WriteLine("Alternative exit"); 88: 89: End: 90: break; // Прерывание цикла. 91: } 92: 93: // Delay. 94: Console.ReadKey(); 95: } 96: } 97: }
Вот у нас идет, не пугайтесь, это еще проще Дейкстры. Давайте мы посмотрим. У нас тоже на Википедии есть описание цикла Паук. Это просто как разновидность циклов. Вы это сможете найти на этой страничке. Где идет общее описание понятия циклов в программировании. Здесь же и описывается цикл Дейкстры, и здесь описывается цикл Паук. Поэтому я рекомендую вам тоже немного почитать и поразмышлять над этими конструкциями. Хорошо. Давайте посмотрим ка строится цикл паук, а потом вернемся и почитаем о нем комментарии и прокомментируем. На 47 строке мы создаем символьную переменную, на 49 строке мы создаем некий флаг flag, Который будет индикатором для какого-то состояния. Мы не знаем для какого. Обратите внимание, у нас и переменная называется flag. Что такое флаг в программировании. Обычно флагом может являться либо один бит, либо один байт. Почему? Потому что бит самостоятельно мы использовать не можем. Мы его можем использовать только в контексте байта. Но в современном программировании – это очень сложная конструкция, которая будет выделять бит и контролировать его. Хотя есть механизмы, которые позволяют работать проще с такими требованиями. Так вот флаг, почему он в программировании называется флаг, флаг может быть либо поднят, либо спущен. Если флаг поднят – это единица. Если флаг спущен – это 0. Когда такое происходит. Представьте себе королевскую яхту. Такая двухпалубная яхта королевы Великобритании. Только королева заходит на яхту, королевский штандарт взлетает и все видят, что королева сейчас на яхте. А что происходит в ее замке. Флаг спускают. Все видят, что королевы в замке нет. Только королева сошла с яхты, ее штандарт опускается. И все в Лондоне видят, что королевы на яхте нет. Пока она ехала до своего дворца, только она въезжает, дворецкий сразу поднимает ее флаг. Все, королева во дворце. Флаг, это просто индикатор. Есть ли королева на яхте или нет. Система в таком состоянии сейчас или нет. Хорошее у меня настроение – поставил синий флажок, и все ходят и видят, что у меня такое-то состояние. Теперь смотрим, ключевое слово default. Обратите внимание, у него есть круглые скобки и мы ему передаем тип bool. Default переводится как – по умолчанию. И вот этот default имеет возвращаемое значение по умолчанию для каждого типа. Вот для булевых типов по умолчанию какое значение? False. Поэтому в данном случае default вернет что? False. Это условно просто чтобы показать ключевое слово default. Теперь смотрим, на 51 строке мы снова создаем бесконечный цикл for. Мы его могли бы заменить чем? While (true). На 53 строке мы создаем… А что это такое? Мы это изучали? Да, это метка перехода, значит дальше нам встретятся операторы goto. На 54 строке мы флаг еще раз сбрасываем и предлагаем пользователю ввести какое-то число. Давайте посмотрим, мы можем ввести l, r, x и q. То есть у нас есть первый swich и второй swich. C этим swich все понятно, он нам с алгоритма Дейкстры знаком. Идут постоянные выражения l и r и здесь переходим вправо и влево, двигаем допустим нашим человечком игровым. Каким-нибудь аниматом. Выводим на экран «Go left». Флагу присваиваем true. Это значит, что все-таки у нас сработала охраняемая команда. Break – выходим из swich. Этот break выводит нас из swich. И мы попадаем сюда. Флаг чему равен? True. Идем дальше. Если флаг равен true, то мы делаем безусловный переход на метку Strart. А если я хочу допустим выйти. Значит я должен ввести либо x, либо q. Я ввожу, например, x. Значение выражение селектора у меня не совпадает ни с одним из постоянных выражений. Идем дальше. Значение выражения селектора совпадает с постоянным выражением второго case, и поэтому у меня на экран выводится exit и мы идем на метку перехода в область старших адресов. В End и здесь у меня срабатывает что? Break. А он выводит нас из бесконечного цикла for. Обратите внимание, это уже мы не сколько рассматриваем конструкцию, сколько мы рассматриваем некий шаблон. Маленький, маленький паттерн. Небольшое правило, как мы можем использовать циклы, когда у нас имеются некие охраняемые команды. Когда у нас имеются несколько ветвей возможных вариантов завершения и вот здесь нам помогает цикл Паук. Ну а мы идем дальше, дальше смотри в пример цикла Паук. Мы с вами забыли комментарии прочитать. Давайте посмотрим, чтобы вы уже самоутвердились. Цикл Дейкстры не содержит явного условия продолжения или выхода. Поэтому была предложена усложнённая конструкция цикла Дейкстры, получившая название "цикл-паук". Здесь тоже идет маркер начала, маркер завершения, охраняющее условие и охраняемая команда. После маркера out добавлены ветви завершения, состоящие из условий выхода Qi и команд завершения Ti. Видите. Этот текст читается вами уже легко, соответственно этот текст вы можете найти и в других источниках, может быт там он немного перефразирован, где-то очень похож, потому что все описания очень похожи друг н друга. И поэтому я думаю, что вам будет легко теперь читать даже ту же самую техническую литературу, которая описывает программные конструкции. Хорошо. А мы переходим теперь к следующему. Упрощенный цикл Паук.
1: using System; 2: 3: // Упрощенный цикл Паук (Spider). 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: char character = '\0'; 12: 13: for (; ; ) 14: { 15: character = Console.ReadKey().KeyChar; 16: 17: switch (character) 18: { 19: case 'l': // 'l' - охраняющее условие 1. 20: { 21: Console.WriteLine("Go left"); continue; // охраняемая команда. 22: } 23: case 'r': // 'r' - охраняющее условие 2. 24: { 25: Console.WriteLine("Go right"); continue; // охраняемая команда. 26: } 27: } 28: 29: switch (character) 30: { 31: case 'x': // 'x' - условие выхода 1. 32: { 33: Console.WriteLine("Exit"); break; // команда завершения. 34: } 35: case 'q': // 'q' - условие выхода 2. 36: { 37: Console.WriteLine("Quit"); break; // команда завершения. 38: } 39: default: // Ветвь альтернативного завершения. 40: { 41: Console.WriteLine("Alternative exit"); break; 42: } 43: } 44: 45: break; // Прерывание цикла. 46: } 47: 48: // Delay. 49: Console.ReadKey(); 50: } 51: } 52: }
На 11 строке создаем символьную переменную character. На 13 строке создаем… Сколько здесь итераций? Это бесконечный цикл for. Видите, как интересно, просто ставим пару точек с запятой. На 15 строке предлагается ввести какое-то значение. Значит, мы видим, что у нас имеются охраняемые команды, охраняемое два выражения, состоящие с охраняющего условия и охраняющей команды. Если введу l либо r выполнятся эти блоки. Если я введу x либо q у меня выполнятся вот эти команды завершения. Если же я введу что-то отличное от l, r, x и q. У меня выполнится необязательный блок default. Обратите внимание, а что делает необязательный блок default? Он просто выкинет нас из этого swich и мы попадем в тело цикла for, а здесь нас встретит оператор break, который и прекратит выполнение бесконечного цикла. Поэтому мы понимаем, что благодаря этому break данная программа у на не зависнет. И цикл for не будет являться бесконечным циклом. Не смотря на то, что на 13 строке мы его изначально задали как псевдобесконечный. Это упрощенный цикл Паук. Сравните их. И последний пример.
1: using System; 2: 3: // Бесконечные циклы. 4: 5: namespace Loop 6: { 7: class Program 8: { 9: static void Main() 10: { 11: // 1. 12: while (true) 13: { 14: } 15: 16: 17: // 2. 18: do 19: { 20: } 21: while (true); 22: 23: 24: // 3. 25: for (; ; ) 26: { 27: } 28: 29: 30: // Delay. 31: Console.ReadKey(); 32: } 33: } 34: }
Последний пример просто показывает варианты создания бесконечных циклов. Вот как у нас создается бесконечный цикл while, бесконечный цикл do-while, обратите внимание, мы в условии просто безусловно ставим значение истины. И вот цикл for. Замечательно. Давайте вернемся к нашей презентации и подведем итог. Тема сегодняшнего урока была – циклические конструкции. Мы рассмотрели три основных циклических конструкции: while с предусловием, do-while с постусловием, for с счетчиком. Давайте перейдем посмотрим по презентации что мы с вами смотрели. Прочитайте определение. Далее мы познакомились с оператором goto, который так ругал Дейкстра, не рекомендовал их использовать. Да, вы пока не используйте, вы пока не знаете, как им правильно пользоваться. Поэтому старайтесь его знать, сами себе поиграйтесь, посмотрите на его работу, но старайтесь в промышленном коде пока его не использовать. Далее цикл с предусловием while, цикл с постусловием do-while, первая итерация цикла выполняется безусловно. И цикл со счетчиком for. На этом наш урок закончен. Спасибо за внимание. До новых встреч.