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

Программирование на языке C# Консольные и оконные приложения. Лабораторный практикум. Учебное пособие

.pdf
Скачиваний:
1
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
☆
В первом операторе описывается и инициируется значениями одномерный массив, во втором – двумерный массив.
Необходимо понимать, что в C# под массивы память выделяется только динамически. Но в отличие от других языков высвобождать память не требуется, этим занимается среда.
Операторы цикла в языке C#
Для многократного повторения некой последовательности действий (набора операто­ров) используются операторы цикла. В языке C# таких операторов несколько.
Для повторения действий, пока выполняется определённое условие, используется опе­ратор while. Синтаксис оператора while выглядит следующим образом:
while (условие) оператор;
По форме и действию он схож с сокращённой формой условного оператора if. Отли­чие в том, что оператор в конструкции while выполняется до тех пор, пока условие не ста­нет ложью. Оператор часто является составным. Он называется «тело цикла», которое в кон­струкции while должно быть таким, чтобы изменять значение условия, т. к. других испол­няемых операторов в этот момент нет.
Оператор do…while (аналогичный оператору while) имеет формат:
do оператор
while (условие)
Отличается от while тем, что здесь условие проверяется после выполнения тела цик­ла. Другими словами, тело цикла в конструкции do…while будет обязательно выполнено, хотя бы один раз; а в конструкции while – может не выполниться ни разу.
С обработкой массивов тесно связан оператор повторения for. Синтаксис этого опера­тора выглядит следующим образом:
for (выражение1; условие; выражение 2) оператор;
Оператор for можно воспринимать как некую интегральную конструкцию оператора while. Он эквивалентно может быть записан конструкцией while с сопутствующими опе-
раторами. Но, как правило, именно его используют для обработки элементов массива. Тогда выражения 1 и 2 обрабатывают, а условие проверяет целочисленную переменную, кото­рая используется для индексирования ячеек массива.
Для обработки
двумерного массива часто используют вложенные циклы for – для ну­мерации строк и элементов в строке; или наоборот – для перебора колонок и элементов в ко­лонке.
Среди операторов цикла также имеются более интегральные конструкции – это опера-
тор forearch. Синтаксис оператора следующий:
foreach (<тип> имя_переменной_цикла in имя_массива) оператор;
Его заголовок и смысл более просты – выполнить действия для каждого элемента мас­сива. Поскольку часто обработке подвергаются именно все элементы массива, то он логиче­ски подходит для организации такого перебора. Но в более сложных алгоритмах он не так гибок, как операторы for или while.
Стандартные алгоритмы работы с массивами
Сложные алгоритмы
по обработке элементов массивов обычно являются комбинацией
типовых простых алгоритмы. К последним можно отнести алгоритмы для решения таких за­дач, как:
поиск суммы или произведения элементов массива;
поиск максимального элемента в массиве;
поиск номера максимального элемента в массиве;
формирование нового массива по какому-либо условию.
Алгоритмы решения этих задач обычно записываются в один цикл, с использованием условных операторов.
21
Алгоритм поиска суммы элементов заданного массива
Рассмотрим этот алгоритм на примере нахождения суммы 10 элементов массива
, , ..., .aa a
01 9
Для накопления искомой суммы опишем переменную S. Математический путь
последовательного формирования суммы:
, , ..., .0, SSaSaaSaa a  
001 01 9
Очевидно, что на каждом этапе S увеличивает свое значение на величину очередного элемента – к старому значению S добавляется значение очередного элемента и сумма S ста­новится новой. Это накопление S в программе можно записать как
SSa
,
где i – теку-
i
щий номер элемента, он изменяется от 0 до 9. Причем первоначально S = 0.
Пусть массив вещественных данных уже описан и инициализирован ранее. Тогда с по­мощью операторов цикла реализовать на С# поиск суммы можно, например одним из следу­ющих способов:
double S = 0; for (int i=0; i < 10; i++) S = S + a[i];
double S = 0; int k = 0; while (k < 10) { S += a[k]; k++;};
double S = 0; int k = 0; do { S += a[k]; k++; } while (k < 10);
double S = 0; foreach (double g in a) S += g;
Здесь показано использование различных операторов цикла для решения одной и той же задачи. В операторах суммирования использованы как полные конструкции, так и ком­пактные формы записи, характерные для C#.
То, что здесь решение с использованием оператора while и оператора do…while по­чти одинаково, – редкий случай. Часто алгоритмическое решение с использованием одного оператора намного удобнее, чем посредством другого.
Алгоритм поиска максимального элемента в заданном массиве
Одним из классических базовых алгоритмов является поиск максимального (или мини­мального) значения среди элементов массива. Рассмотрим построение такого алгоритма на при-
мере массива
, , ..., .aa a
01 9
Для хранения промежуточного и окончательного значения искомой
максимальной велечины опишем переменную амах.
Идея алгоритма состоит в следующем:
переменная amax инициализируется неким начальным значением;
сравнивается значение в amax со значением очередного элемента массива. Если зна-
чение в элементе a[i] больше, чем в amax, то это значение a[i] становится новым эта­лонным, т. е. сохраняется в amax.
Другими словами, переменная amax играет роль некоего эталона. При этом её значение увеличивается по мере просмотра элементов массива, если
встретились значения большие,
чем ранее просмотренные.
Для предложенного массива требуется повторить 10 раз операцию сравнения. И тогда поиск максимального элемента можно записать на C# с помощью, например, одной из кон­струкций:
double amax = a [0]; for (int i = 1; i < 10; i++)
if (amax < a[i]) amax = a[i];
double amax = a[0]; int k = 1;
while (k < 10)
{ if (amax < a[k]) amax = a[k]; k++; };
double amax = a[0]; int k = 1;
do
{ if (amax < a[k]) amax = a[k]; k++; }
while (k < 10) ;
double amax = a[0];
foreach (double g in a)
if (amax < g) amax = g;
22
Здесь показаны разнообразные формы записи эквивалентных по результату конструк-
x
y
x
ций. Но надо учитывать, что в реальных задачах предварительная инициализация перемен­ной amax значением первого элемента массива a[0] не всегда приемлема. Другими слова­ми, иногда необходимо эту переменную инициировать другим значением, например боль­шим по величине отрицательным.
Алгоритм поиска номера максимального элемента в заданном массиве
Данный алгоритм тесно связан с алгоритмом поиска максимального элемента в массиве. Действительно, помимо максимального значения необходимо найти и его место – это индекс элемента с максимальным значением. Для индекса (или номера) опишем целочисленную пе­ременную n_max. В модифицированном алгоритме индекс будет изменяться тогда же, когда
будет изменяться значение амах. Другими словами, рядом с формированием амах нужно поставить оператор формирования переменой индекса n_max, например:
double amax = a [0]; int n_max = 0;
for (int i = 1; i < 10; i++)
if (amax < a[i]) {amax = a[i]; n_max = i;}
Легко заметить, что данный алгоритм (и код программы) отличается от первого алго­ритма только удвоением действий в теле цикла – теперь изменяются и amax, и n_max.
Но цикл вида foreach здесь не приемлем, т. к. нет переменной, явно указывающей индекс очередного элемента массива.
Алгоритм формирования нового массива по какому-либо условию
Часто из имеющегося большого массива требуется отобрать элементы в другой массив. Рассмотрим образец такого алгоритма на примере задачи.
Задача. Данный массив чисел
, , ...,aa a
01 9
разбить на два. В один включить положи-
тельные числа, в другой – отрицательные.
, , ...,
Обозначим массив положительных чисел
, , ...,
yy
01
. Например, массив –2, 5, 7, –67, –87, 0, 2, 9, –44, 7 должен разделиться на масси-
m
вы9: 5, 7, 2, 9, 7 и –2, –67, –87, –44. Алгоритм заключается в следующем: каждый элемент
исходного массива сравнивается с нулем, т. е. выполняется операция:
всех i = 0, 1, …, 9. Если
a
> 0, то элемент записывается в массив х, если же
i
мент записывается в массив y. Запись производится следующим образом:
xx
01
, а массив отрицательных – через
k
a
сравнить с 0, для
i
a
< 0, то эле-
i
a
ki
или
ya
mi
a
Отметим, что для нумерации элементов новых массивов пришлось ввести дополнительные индексы k и m. Так сделано потому, что порядок изменения индексов в каждом из массивов отличается. Реализация на C# может быть следующей:
Листинг. Пример копирования значений элементов массива в два новых:
k = 0; m = 0;
for (int i = 0; i < 10; i++)
if (a[i] > 0) x[k++] = a[i];
else
if (a[i] < 0) y[m++] = a[i];
i
.
Следует понимать, что до окончания работы цикла нельзя сказать, сколько в массиве положительных и сколько отрицательных элементов. Поэтому массивы x и y должны быть описаны той же длины, что и исходный массив a. По окончании цикла массивы x и y будут не полностью заполненными (а возможно, что один из них окажется пуст). Значения пере­менных k и m и скажут, сколько переменных в каждом из массивов.
9
Здесь рассматривается алгоритм, выделяющий исключительно положительные и отрицательные числа. Нуле-
вые элементы не попадут в новые массивы.
23
Переменные k и m в подобных алгоритмах на языке C# играют двоякую роль. Это и ин­декс очередной свободной ячейки в массиве, и количество элементов в массиве (количество заполненных ячеек).
В данном примере элементы со значением 0 не попали ни в какой новый массив. Необ­ходимо ли включать 0 в новый массив? Это следует решать при разработке алгоритма, исхо­дя из условий заказчика.
Контрольные вопросы
1. Как описать одномерный массив вещественного типа?
2. Как заполнить значениями элементы массива при его описании?
3. Каков формат оператора повторения while?
4. Каков формат оператора повторения for?
5. Каков формат оператора повторения do while?
Варианты заданий
Используя операторы цикла, решите задачу, соответствующую вашему варианту.
Вариант 1
1. Найти максимальный элемент из элементов массива, кратных числу 3, и сумму от-
рицательных элементов.
, , ...,
2. Дано натуральное число n, целые числа
aa a
01
n
. Среди чисел имеется группа из
отрицательных чисел, расположенных подряд. Переписать эту группу в отдельный массив. Если группы нет, то выдать сообщение об этом. Если таких групп несколько, выдать послед­нюю группу.
Вариант 2
1. Найти максимальный элемент из положительных элементов массива и сумму эле-
ментов, кратных числу 7.
, , ...,
2. Дано натуральное число n, целые числа
aa a
01
n
. В новый массив включить чис-
ла, которые расположены после последнего нулевого элемента при условии, что они кратны числу 3. Если нулевых элементов нет, то выдать сообщение об этом.
Вариант 3
1. Найти минимальный элемент из элементов массива, кратных числу 5, и сумму отри-
цательных элементов.
, , ...,
2. Дано натуральное число n, целые числа
aa a
01
n
. Массив чисел представлен группа-
ми одинаковых элементов, идущих подряд. Кроме этого, имеется группа из возрастающих эле­ментов, также расположенных подряд. Переписать эту группу в отдельный массив. Если группы нет, то выдать сообщение об этом. Если таких групп несколько, выдать первую группу.
24
Лабораторная работа № 5
Использование строковых переменных для решения задач
Цель работы: получить практические навыки в разработке алгоритмов и программ с использованием массивов и переменных типа «строка».
Методические указания
Помимо массивов чисел, целых или вещественных, универсальные языки программи­рования позволяют оперировать с массивами символов, где элементами являются коды букв, цифр и других элементов, которые могут отображаться на экране компьютера. В такие мас­сивы могут входить и символы (их коды), не отображаемые на экране, а также специальные символы, такие как «перевод строки», «табуляция» и т. п.
В некоторых языках, например Pascal, для массивов символов есть отдельный тип
string. В языке C++ используются и типы из языка-предшественника C, и новые классы типа string, где данные и операции соединены воедино. Здесь уже можно оперировать и элемен-
тами массива символов и со всей строкой как с единым целым.
В языке C#, интегрирующем большое число конструкций ООП, помимо конструкций из языков-предшественников C и C++, есть и новая конструкция – класс типа строка string. Дело в том, что язык C# уже активно использует .NET технологии, в частности новые спосо­бы работы с памятью. А старые классы не рассчитаны на такое взаимодействие с новыми технологиями. Поэтому для работы со строками рекомендуется использовать именно пере­менные типа StringBuilder. Но если программисту удобно в некоторых ситуациях использо­вать именно типы строк старого образца, то всегда можно скопировать данные из массива одного типа в другой и выполнить преобразования над таким массивом.
В языке C# строки типа string представляют собой неизменяемые объекты: после того как строка инициализирована, изменить её уже нельзя. Например, код
string s = "Hello, "; s += "world!";
требует создания в памяти строкового объекта с текстом «Hello, », а затем создания другого строкового объекта со значением «Hello, world!». Новая строка s содержит именно то, что хо­тел программист, однако в памяти остаётся и блок памяти со строкой «Hello, », но на него нет ссылок из программы. И таких бесхозных блоков может оказаться очень много. Это негативно сказывается на производительности программы и объеме потребляемой ей памяти.
Чтобы компилятор не создавал каждый раз новый строковый объект, рекомендуется использовать другой строковый класс: StringBuilder. Приведённый выше пример с ис­пользованием этого класса будет выглядеть следующим образом:
StringBuilder s = new StringBuilder("Hello, ");
s.Append("world!");
Этот код выглядит более громоздким, но в программе он будет гораздо эффективнее. Помимо команды Append добавления текста к существующему объекту, класс StringBuilder имеет ещё ряд полезных методов:
Insert – вставить указанный текст в нужную позицию исходной строки;
Remove – удалить часть строки;
Replace – заменяет указанный текст в строке на другой текст.
Если нужно обрабатывать текст из объекта StringBuilder как обычную строку string, тогда для этого и копируют содержимое в строку типа string, используя метод ToString(), например:
StringBuilder s = new StringBuilder("Яблоко"); string a = s.ToString();
Другими словами, вся работа с текстом может выполняться в строке типа StringBuilder, а при необходимости это может быть скопировано и обработано в пере­менной типа string.
25
Контрольные вопросы
1. Для чего предназначены переменные типа string и StringBuilder?
2. Как описать переменную типа StringBuilder и заполнить её начальным текстом?
3. Какие основные команды по работе с переменной типа StringBuilder вы знаете?
Варианты задания
Для практического закрепления навыков разработки алгоритмов использования услов­ного оператора напишите три короткие программы.
Вариант
1
1. Дан целочисленный массив. Если какой-то элемент встречается в нем более одного
раза, то первое вхождение оставить без изменения, второе – «удвоить», третье – «утроить» и т. д. Например, из массива 2, 13, 4, 13, 6, 8, 8, 13 получится массив 2, 13, 4, 1313, 6, 8, 88,
131313.
2. Дан массив символов, среди которых имеются символы – цифры. Собрать рядом
расположенные цифры в числа и сложить. Например, из символов «апрель45май31» нужно получить число 76, т. е. получить массив целых чисел и просуммировать их.
Вариант 2
1. Дан целочисленный массив. Если какой-то элемент встречается в нем более одного
раза, то оставить только первое вхождение. Например, из массива 2, 13, 4, 13, 6, 8, 8, 13 получится массив 2, 13, 4, 6, 8.
2. Дан массив символов, среди которых имеются символы – цифры. Последние рядом
расположенные цифры необходимо собрать в число. Например, из символов «ап21ре4ль111май312июль» нужно получить число 312 и занести его в переменную целого типа.
Вариант 3
1. Дан целочисленный массив, в котором есть два числа со значением Н, одно из кото-
рых находится на последнем месте (имеются две подпоследовательности, заканчивающиеся на Н). Поменять местами эти подпоследовательности. Например, из массива: 2, 13, 4, 53, 6, 8,
8, 13, 8, 4, 18, 76, 53 при условии, что Н = 53, нужно получить: 6, 8, 8, 13, 8, 4, 18, 76, 53, 2, 13, 4, 53.
2. Дан массив символов, среди которых имеются символы – цифры. Собрать рядом
расположенные цифры в числа. Например, из символов «ап21ре4ль111май312июль» нужно получить числа 21, 4, 111, 312, т. е. массив целых чисел с такими значениями.
26
Лабораторная работа № 6
Проектирование и разработка программ со структурами данных
Цель работы: получить практические навыки реализации простейших программ, ис- пользующих данные типа «структура».
Глоссарий
Структура – тип переменных, определяемый пользователем; объединяет набор раз-
нотипных данных, объединенных логически и воспринимаемых как единое целое.
Поле структуры – отдельный элемент структуры, содержащий единственное данное
(базового типа), или массив однотипных данных, или другую структуру.
Тег структуры – имя структуры; имя нового типа.
Методические указания
Структура – один из примеров составленных типов данных, которые иначе называют агрегатными типами, или агрегатами
Если массив представляет собой последовательность однотипных данных, то структура может быть образована из неоднородных компонент (это могут быть простые переменные, массивы, структуры, функции и т. д.). Более того, в структуре явным образом выражается идея иерархической подчиненности между элементами. Логически вся совокупность данных, относящаяся к одному объекту11 и описанная в программе как структура, будет сконцентри­рована в одном месте и логически, и в оперативной памяти.
Как и любая переменная других типов, структурная переменная должна описываться. Но здесь описание состоит из двух шагов:
1) задание шаблона структуры (собственно описание структуры нового типа);
2) собственно описание одной или нескольких структурных переменных.
Использование переменных типа «структура» позволяет сосредоточить значения (пере­менные базовых типов, массивы или типа «структура»), описывающие один объект, в один блок.
10
.
Структуры в языке C#
В языках, базирующихся на синтаксисе языка Си, под термином «структура» понима­ются несколько различные объекты. Если в языке Pascal оператор record (аналог структу­ры) включает исключительно переменные разных типов, то C-подобных языках в нее входят и методы обработки элементов структуры. В некоторых языках этот тип очень близок к типу
class.
По-другому структура – это более простая версия классов. Все структуры наследуют­ся от базового класса System.ValueType и являются типами значений, тогда как классы –
ссылочные типы.
Структуры подходят для создания несложных типов, таких как точка, цвет, окруж­ность. Если необходимо создать множество экземпляров подобного типа, используя структу­ры, мы экономим память, которая могла бы выделяться под ссылки в случае с классами.
Примерами структур в стандартной библиотеке классов .Net являются такие типы, как
int, float, double, bool и др., а также DateTime, Point (точка), Color.
Поэтому в C# для преобразования типов приходится обращаться к методам этой струк­туры, например ivar.ToString(); (здесь ivar переменная целого типа int), а не к внешним стандартным функциям, как в C или C++, например svar = itoa (ivar); описанным в этой про­грамме.
В рамках лабораторных работ предлагается использовать простейший вариант структур – агрегирование только разнотипных переменных.
10
Но язык C++ не имеет типа «структура», а имеет инструмент, позволяющий создавать пользователю новые
типы, группируя разнотипные данные, и использовать этот новый тип в программе.
11
Имеется в виду материальный объект или объект другой природы.
27
Описание структуры
Описание нового типа начинается ключевым словом struct, после которого в фигур­ных скобках располагается список полей с указанием типов. Поля могут быть любого типа, определенного в C++: целого, вещественного, символьного, массивы. Также поле может быть типа структуры, определенной ранее по тексту программы или в заголовочном файле .H или .HPP.
Ниже
приведен пример описания или задания шаблона структуры с тегом (именем)
spisok:
public struct spisok
{
public int kod; // код покупателя
public string fio; // фамилия покупателя
public int[] data; // дата покупки (месяц , год)
public double[] result; // суммы, выплачиваемые за кварталы года
public double rest; // остаток суммы платежа
}
Здесь kod, fio и rest – простые поля, а data и result – поля-массивы. Задание шаблона или описание структуры вводит новый тип данных.
Описание структурной переменной
После задания шаблона можно объявлять переменные вновь созданного типа. Описа­ние структурной переменной состоит из задания типа (тега) и имени структурной перемен­ной. Например, требуется объявить две переменные form1 и form2 типом spisok. Это можно сделать оператором описания типа:
spisok form1, form2;
Использование структурной переменной
Чтобы обратиться к элементу структуры (полю), достаточно к имени переменной доба­вить точку и имя конкретного поля структуры (точечная нотация):
а) обращение к простым полям:
form1.kod
form2.rest
б) обращение к элементу массива поля result:
form2.result[0]
form2.result[3]
Использование массивов структур
Если переменная объявлена как массив структур, например
spisok[] form7 = new spisok[6];
то обращение к элементу массива структур включает индекс:
form7[4].kod
form7[i].kod
form7[i].result[0]
В последнем примере обращение идет к ячейке с индексом 0 массива result, который является частью элемента с индексом i массива структур form7.
Пример программы, использующей переменные типа «структура», приведен в приложении.
Контрольные вопросы:
1. Что такое «структура»? Каков общий вид?
2. В каких случаях предпочтительно использовать структуру?
3. Что может быть полем структуры?
4. Как использовать поля структуры в вычислениях?
5. Как изменятся параметры у функции, если она станет полем структуры?
28
Варианты заданий
Для практического закрепления навыков использования переменных и массивов типа
«структура» напишите три короткие программы.
Вариант 1
Дан массив структур, в котором содержатся сведения о товарах, находящихся на скла­де: наименование, объем партии, дата поступления на склад, стоимость единицы товара. Требуется подсчитать общую сумму товара.
Вариант 2
Дан массив структур,
в котором содержатся сведения о книгах, находящихся в читаль­ном зале библиотеки: ФИО автора, название, наименование издательства, год издания, стои­мость. Требуется подсчитать общую стоимость книг.
Вариант 3
Дан массив структур, в котором содержатся сведения о свободных местах в поездах по всем направлениям на ближайшую неделю: дата отправления, номер рейса, конечный пункт назначения, время отправления, число свободных плацкартных мест. Требуется подсчитать суммарное количество свободных мест.
29
Лабораторная работа № 7
Проектирование и разработка программ с массивами данных
Цель работы: получить практические навыки реализации простейших программ, ис- пользующих массивы, для хранения и обработки данных в системе MS VS C#.
Глоссарий
Массив – тип переменных, который объединяет набор однотипных данных.
Одномерный массив – линейный список однотипных элементов; обращение осу-
ществляется по единому имени (имени массива) и индивидуальному индексу элемента.
Двухмерный массив – список однотипных элементов в виде таблицы; обращение
осуществляется по единому имени (имени массива) и двойному индивидуальному индексу элемента.
Методические указания
При решении задач и разработке программ в системе Microsoft Visual Studio C#, так же как и в других системах, часто используются одномерные и многомерные массивы.
Простое описание одномерного массива, каждый из элементов которого имеет один и тот же <тип>, в C# выполняется следующим образом:
<тип>[] <имя> = new <тип> [<количество>];
Пример:
double[] mass = new double [100];
описывает массив mass из 100 элементов, каждая ячейка которого имеет тип double; при этом (необходимо помнить) элементы индексируются в диапазоне от 0 до 99.
Обращение к элементу двумерного массива выполняется следующим образом:
<имя> [<индекс элемента]
Обращение к элементам массива может быть и через значение целочисленной пере­менной12. Например, к элементам этого массива можно обращаться:
mass [i] mass [j] mass [33]
где i и j – переменные одного из целочисленных типов, описанные ранее.
Описание двумерного массива в системе MS VS C# выполняется следующим образом:
<тип элемента>[,] имя [<кол-во строк>, <кол-во колонок>];
Обращение к элементу двумерного массива выполняется следующим образом:
<имя> [<индекс строки][индекс колонки].
Описание двумерного массива:
double[,] matr = new double [100, 200];
создает двумерный массив matr из 100 строк и 200 элементов в каждой строке (или 200 ко­лонок), каждая ячейка которого имеет тип double, при этом строки индексируются в диапазоне от 0 до 99, а элементы – в стоке от 0 до 199. Обращение к ячейкам такого массива аналогично одномерному, выполняется по имени массива и индексу строки и колонки (или элемента). В качестве индекса могут использоваться константа, константное выражение, переменная или выражение целого типа, например, matr [i,j]. Здесь i и j должны быть в диапазонах, ука­занных выше.
Для обработки элементов массивов часто используют операторы повторения или даже несколько таких операторов, вложенных друг в друга.
12
Но значение такой переменной должно быть в пределах размера массива. Иначе получаем алгоритмическую
ошибку – неправильный алгоритм. Иногда происходит сбой программы на этапе выполнения, что, к счастью, ярче показывает наличие ошибки. Однако необнаруженные ошибки также опасны.
30
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]