Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование. Введение в разработку на C#. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
4.2. Виды циклов
4.2.1. Безусловные циклы
Часто в программах применяются циклы, выход из которых не пре­дусмотрен программной логикой. Такие циклы называются безуслов­ными, или бесконечными. Специальных синтаксических конструкций для создания бесконечных циклов, ввиду их узкой направленности, языки программирования не предусматривают, поэтому такие циклы создаются с помощью конструкций, определённых для создания клас­сических (или условных) циклов. Для обеспечения бесконечного по­вторения проверку условия в таком цикле либо убирают, либо заменя­ют константным значением:
while(true) Console.WriteLine("Меня не остановить!");
4.2.2. Цикл с предусловием
Цикл с предусловием – цикл, который выполняется, пока истинно некоторое условие, указанное перед его началом. Это условие проверяется до выполнения тела цикла, поэтому тело может быть не выполнено ни разу (если условие с самого начала ложно). В большинстве процедурных языков программирования цикл с предусловием реализуется оператором while, отсюда его второе название – while-цикл. Его пример показан выше. Если количество операций превышает одну, то необходимы фигурные скобки.
4.2.3. Цикл с постусловием
Цикл с постусловием – цикл, в котором условие проверяется после выполнения тела цикла. Отсюда следует, что тело всегда выполняется хотя бы один раз:
int i = -2;
do { i++;
41
}
while(i > 0);
4.2.4 Цикл с выходом из середины
Цикл с выходом из середины – наиболее общая форма условного цикла. Синтаксически такой цикл создается с помощью трёх конструкций: начала цикла, конца цикла и команды выхода из цикла. Конструкция начала определяет точку программы, в которой начинается тело цикла, конструкция конца – точку, где тело завершается. Внутри тела должна находиться команда принудительного выхода из цикла, при выполнении которой цикл завершается и управление передаётся на оператор, следующий за конструкцией конца цикла. Естественно, чтобы цикл выполнялся более одного раза, команда выхода должна вызываться не безусловно, а только при выполнении условия выхода из цикла.
Принципиальным отличием такого вида цикла от рассмотренных выше является то, что часть тела цикла, расположенная после начала цикла и до команды выхода, выполняется всегда (даже если условие выхода из цикла истинно при первой итерации), а часть тела цикла, на­ходящаяся после команды выхода, не выполняется при последней ите­рации.
Легко заметить, что с помощью цикла с выходом из середины мож­но моделировать и цикл с предусловием (разместив команду выхода в начале тела цикла), и цикл с постусловием (разместив команду выхода в конце тела цикла):
int i = 0; while(i < 10)
{ i++;
if (i == 5)
break; }
42
4.2.5. Цикл со счётчиком
Цикл со счётчиком – цикл, в котором некая переменная меняет своё значение от заданного начального до конечного значения с заданным шагом, и для каждого значения этой переменной тело цикла выполняется один раз. В большинстве Си-подобных языков программирования реализуется оператором for, в котором указывается счётчик (так называемая «переменная цикла»), требуемое количество проходов (или граничное значение счётчика) и, возможно, шаг, с которым изменяется счётчик:
for(int i = 0; i < 10; i++) Console.WriteLine(i)
4.2.6. Совместный цикл
Ещё один вариант цикла – цикл, задающий выполнение некоторой операции для объектов из заданного множества, без явного указания по­рядка перечисления этих объектов. Такие циклы называются совместны­ми (а также циклами по коллекции, циклами просмотра) и представляют собой формальную запись инструкции вида: «Выполнить операцию X для всех элементов, входящих в множество M». Совместный цикл теоретиче­ски никак не определяет, в каком порядке операция будет совершаться по отношению к элементам множества, хотя конкретные языки программи­рования, разумеется, могут задавать конкретный порядок перебора эле­ментов. Произвольность даёт возможность оптимизировать исполнение цикла за счёт организации доступа не в заданном программистом, а в наиболее выгодном с точки зрения логики порядке. При наличии воз­можности параллельного выполнения нескольких операций возможно даже распараллеливание выполнения совместного цикла, когда одна и та же операция одновременно выполняется на разных вычислительных мо­дулях для разных объектов, при том что логически программа остаётся последовательной.
43
Совместные циклы имеются во многих современных языках программирования (C#, Eiffel, Java, JavaScript, Perl, Python, PHP, LISP, Tcl и др.) – они позволяют выполнять цикл по всем элементам заданной коллекции объектов. В определении такого цикла требуется указать только коллекцию объектов и переменную, которой в теле цикла будет итерационно присвоено значение обрабатываемого в данный момент объекта (или ссылка на него). В различных языках программирования синтаксис оператора различается. В C# это :
foreach(int i in Enumerable.Range(1, 10)) Con­sole.WriteLine(i);
4.3. Вопросы для самопроверки
1. Каково определение понятия «алгоритм»?
2. Какие требования следуют из определения алгоритма?
3. Поясните любые 3 требования к алгоритму на выбор, приведите
по одному примеру на каждое пояснение.
4. Какие виды циклов бывают? Приведите пример кода на языке C#,
описывающего каждый из видов циклов.
5. Для какого типа цикла не предусматриваются специальные
синтаксические средства в языках программирования?
6. Какие, по вашему мнению, преимущества дает использование
совместного цикла в сравнении с циклом со счетчиком?
4.4. Задания для самопроверки
1. При помощи цикла while переделать задание с инверсией введённой строки таким образом, чтобы программа не завершалась после выполнения операции. Программа должна завершаться, если пользователь ввёл строку "exit".
2. Дан цикл, надо в нём добавить в трех местах постфиксный или префиксный инкремент/декремент так, чтобы программа вывела 0.
44
Например:
int j = 0; for (int i = 0; i < 2; ?i?) { j = ?i?; } Console.Write(?j?);
3. Проверить введенное число на простоту. Число считается простым, если оно делится только на 1 и на себя без остатка.
4. Выведите нечетные числа в диапазоне от 100 до 200.
5. Выведите двоичное представление десятичного числа.
45
5. РАБОТА С МАССИВАМИ
5.1. Одномерные и многомерные числовые массивы
Массивом называют упорядоченную совокупность элементов одно­го типа. Каждый элемент массива имеет индексы, определяющие поря­док элементов. Индексы задаются целочисленным типом. Число индек­сов характеризует размерность массива. Если конечное значение задано константным выражением, то число элементов массива известно в мо­мент его объявления и ему может быть выделена память ещё на этапе трансляции. Такие массивы называются статическими. Если же конеч­ное значение зависят от переменной, то такой массив называют дина­мическим, поскольку память ему может быть отведена только динами­чески в процессе выполнения программы, когда становятся известными значения соответствующих переменных. Массиву, как правило, выде­ляется непрерывная область памяти.
В языке C# каждый индекс изменяется в диапазоне от 0 до некоторого конечного значения. Массивы в языке C# являются настоящими динамическими массивами. Как следствие этого, массивы относятся к ссылочным типам, память им отводится динамически в «куче». Массивы могут быть одномерными и многомерными.
Объявление одномерного массива выглядит следующим образом:
<тип>[] <объявители>;
Как и в случае объявления простых переменных, каждый объявитель может быть именем или именем с инициализацией. В первом случае речь идёт об отложенной инициализации. Нужно понимать, что при объявлении с отложенной инициализацией сам массив не формируется, а создаётся только ссылка на массив, имеющая неопределённое значение Null. Поэтому пока массив не будет реально создан и его элементы не будут инициализированы, использовать его в вычислениях нельзя. Вот пример объявления трёх массивов с отложенной инициализацией:
46
int[] a, b, c;
Чаще всего при объявлении массива используется имя с инициализацией. И, как и в случае простых переменных, могут быть два варианта инициализации. В первом случае инициализация является явной и задаётся константным массивом. Например:
double[] x = {5.5, 6.6, 7.7};
Следуя синтаксису, элементы константного массива необходимо за­ключать в фигурные скобки. Если массив инициализируется константным массивом, то в динамической памяти создаётся константный массив с за­данными значениями, с которым и связывается ссылка.
Во втором случае создание массива выполняется с помощью операции new. Например:
int[] d = new int[5];
Здесь объявлен динамический целочисленный массив, в котором будут храниться 5 целых чисел. Массив создаётся в динамической памяти, его элементы получают начальные нулевые значения, и ссылка связывается с этим массивом.
Примеры:
// объявляются три одномерных массива A,B,C int[] A = new int[5], B= new int[5], C= new int[5]; // заполняется данными с клавиатуры массив A for(int i = 0; i < 5; i++) A[i] = int.Parse(Console.ReadLine());
// вычисляются элементы массива C for(int i = 0; i < 5; i++) C[i] = A[i] + B[i]; // объявление массива с явной инициализацией int[] x ={5.5, 6.6, 7.7}; // объявление массивов с отложенной инициализацией int[] u, v; u = new int[3];
47
for(int i = 0; i < 3; i++) u[i] = i + 1;
// v = {1,2,3}; // присваивание константного массива недопустимо! v = new int[4];
v = u; // допустимое присваивание – массивы одного класса int [,] w = new int[3,5]; // v = w; // недопустимое присваивание: объекты разных классов
В вышеприведённых примерах объявлялись статические массивы, поскольку нижняя граница равна нулю по определению, а верхняя всегда задавалась в этих примерах константой. Вспомним, что в C# все массивы, независимо от того, каким выражением описывается граница, рассматриваются как динамические, и память для них распределяется в «куче». Чисто синтаксически нет существенной разницы в объявлении статических и динамических массивов. Выражение, задающее границу изменения индексов, в динамическом случае содержит переменные. Единственное требование – значения переменных должны быть определены в момент объявления. Это ограничение в C# выполняется автоматически, поскольку хорошо известно, насколько требовательно C# контролирует инициализацию переменных. Рассмотрим пример:
// объявление динамического массива A1
Console.WriteLine("Введите число элементов массива A1"); int size = int.Parse(Console.ReadLine());
int[] A1 = new int[size];
5.2. Класс Array
Нельзя понять многие детали работы с массивами в C#, если не знать устройство класса Array из библиотеки FCL, потомками которого
48
по определению являются все классы-массивы. Рассмотрим следующие объявления:
int[] ar1 = new int[5]; double[] ar2 = {5.5, 6.6, 7.7};
int[,] ar3 = new Int32[3,4];
Зададимся естественным вопросом: к какому типу или к каким классам принадлежат объекты ar1, ar2 и ar3? Ответ прост: все они принадлежат к разным классам. Переменная ar1 принадлежит к классу int[] – одномерному массиву значений типа int, ar2 – к double[] – одномерному массиву значений типа double, ar3 – к двумерному массиву значений типа int. Следующий закономерный вопрос: что общего есть у этих трёх объектов? Прежде всего, все три класса этих объектов, как и другие классы, являются потомками класса Object, а потому имеют общие методы, наследованные от класса Object и доступные объектам этих классов.
У всех классов, являющихся массивами, много общего, поскольку все они являются потомками класса System.Array. Класс System.Array наследует ряд интерфейсов: ICloneable, IList, ICollection, IEnumerable, а следовательно обязан реализовать все их методы и свойства. Помимо наследования свойств и методов класса Object и вышеперечисленных интерфейсов, класс Array имеет довольно большое число собственных методов и свойств. Благодаря такому мощному родителю, над массивами определены самые разнообразные операции: копирование, поиск, обращение, сортировка, получение различных характеристик. Статические методы класса Array позволяют решать самые разнообразные задачи:
1) Copy – позволяет копировать весь массив или его часть в другой
массив;
2) IndexOf, LastIndexOf – определяют индексы первого и последнего вхождения образца в массив, возвращая значение -1, если такового вхождения не обнаружено;
49
3) Reverse – выполняет обращение массива, переставляя элементы в обратном порядке;
4) Sort – осуществляет сортировку массива;
5) BinarySearch – определяет индекс первого вхождения образца в отсортированный массив, используя алгоритм двоичного поиска.
5.3. Пример работы с массивами
Существует несколько часто повторяющихся в различных задачах операций работы с массивами. К таким шаблонным операциям можно отнести:
1) прохождение одномерного массива от начала до конца:
int[] Arr = {1,2,3,4,5};
for (int i = 0; i < Arr.Length; i++)
{
Console.WriteLine(Arr[i]);//Чтение i-того элемента
массива
Arr[i] = 0; // Присвоение значения i-тому элементу
массива
}
2) прохождение многомерно массива от начала до конца по
строкам-столбцам:
int[,] Arr = {{1,2,3}, {4,5,6}, {7,8,9}};
for (int i = 0; i < Arr.GetLength(0); i++) { for (int j = 0; j < Arr.GetLength(1); j++) Console.Write(Arr[i, j] + " "); Console.WriteLine(); }
50
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]