Оператор цикла с перебором foreach

Прямоугольные массивы

Прямоугольный массив имеет более одного измерения.Многомернымназывается такой массив, который характеризуется двумя или более измерениями, а доступ к отдельному элементу осуществляется посредством двух или более индексов.

Двумерные массивы – являются наиболее простыми из многомерных массивов. К тому же и решение очень многих задач может быть часто сведено к обработке двумерных массивов, поэтому технологии использования двумерных массивов в программах языкаС# посвящается целая глава.

В двумерном массиве позиция любого его элемента определяется двумя индексами. Если представить двумерный массив в виде таблицы данных, то один индекс означает строку, а второй – столбец. Чтобы объявить двумерный массив целочисленных значений размером n*m, достаточно записать следующее:

int[,]имя_массива = new int[n,m];

Следует обратить внимание на то, что синтаксически конструкция вида[,]указывает на создание ссылочной переменной двумерного массива.

Чтобы получить доступ к элементу двумерного массива, используется конструкция переменная с индексами, где необходимо указать оба индекса, разделив их запятой. Например, чтобы присвоить элементу двумерного массива, позиция которого определяется координатами i и j, значениеа, можно использовать следующую инструкцию:

имя_массива[i,j] = a;

Многомерный массив можно инициализировать, заключив список инициализаторов каждой размерности в собственный набор фигурных скобок. Формат инициализации двумерного массива:

тип[,] имя_массива = {

{val00, val01, val02, …, val0m}

{val10, val11 val12, …, val1m}

{valn0, valn1, valn2, …,valnm}

};

Здесь элемент valij – значение инициализации для конкретного элемента массива. Каждый внутренний блок означаетстроку. В каждой строке первое значение будет сохранено в первой позиции массива, второе значение – во второй и т.д.

Ниже приведены различные варианты описания двумерного массива.

1) Определены только тип и имя массива, а размер массива и его элементы не заданы. Синтаксис конструкции:

тип[,] имя;

Пример использования для объявления целочисленного массива:

int[,] а;

2) Определены не только тип и имя массива, но и с помощью оператора new заданы обе размерности массива и его элементамприсвоены значения равные 0. Синтаксис конструкции:

тип[,] имя = new тип [ разм_1, разм_2 ];

Пример использованиядля объявления целочисленного массива:

int[,] b = newint [2, 3];

3) Размерность явно не задана, но она вычисляется компилятором по количеству значений в списке инициализации, элементам массива присваиваются значения из списка инициализации:

тип[,] имя = new тип [,] { список_инициализаторов };

Пример использованиядля объявления целочисленного массива:

int[,] c = newint[,]] {{1,2,3),{4,5,6}};

Объявлен массив с типаint, состоящий из двух строк и трех столбцов.

4) Оператор newопущен, но по умолчанию он подразумевается, размерность массива определяется списком инициализаторов, элементам массива присваиваются значения из списка инициализации:

тип[,] имя = { список_инициализаторов };

Пример использованиядля объявления целочисленного массива:

int[,] с = {{1,2,3},{4,5,6}};

5) Избыточное описание– размерность определяется явно и через списокинициализации. Необходимо следить, чтобы полученные двумя путями значения размерностей не конфликтовали между собой.

тип[,] имя = new тип [ разм1, разм2]{ список_инициализации};

Пример использованиядля объявления целочисленного массива:

int[,] d = newint[2,3]] {{1,2,3),{4,5,6}}

Если список инициализации не задан, размерности могут быть не только константами, но и выражениями типа, приводимого к целому. К элементу двумерного массива обращаются, указывая индексы строки и столбца, на пересечении которых он расположен, например:

а[1,4] b[i, j] b[j, i]

Необходимо помнить, что компилятор воспринимает первый индекс как номер строки, второй индекс всегда – индекс столбца,независимо от того,как они ни были обозначены в программе.

Пример 1.Для целочисленной матрицы размером m*n вычислить сумму элементов в каждой строке (рис. 1).

Рисунок 1– Матрица из mстрок и nстолбцов

Нахождение суммы элементов каждой строки требует просмотра матрицы по строкам. Схема алгоритма приведена на рис. 2, программа – в листинге 1.

Листинг 1-Программа к примеру 1

usingSystem;

namespace ConsoleApplication1

{

classClass1

{

staticvoid Main()

{ constint m = 3, n = 4;

int[,] a = newint[m, n] {

{ 2, 2, 8, 9 },

{ 4, 5, 6, 2 },

{ 7, 0, 1, 1 }

};

Console.WriteLine("Исходныймассив:");

for ( inti = 0; i< m; ++i)

{

for ( int j = 0; j < n; ++j )

Console.Write(" " + a[i, j]);

Console.WriteLine();

}

for ( inti = 0; i< m; ++i )

{

int sum = 0;

for ( int j = 0; j < n; ++j )

sum += a[i, j];

Console.WriteLine("Встроке {0}суммаэлементов{1}",i, sum );

}

Console.Read();

}

}

}

 

Рисунок 2– Схема алгоритма к примеру 1

Результат работы программы:

 

Следует обратить внимание на то, что переменная sum обнуляется перед циклом просмотра очередной строки матрицы, поскольку для каждой строки его вычисление начинается заново.

Пример 2.Поиск максимального элемента в двумерном массиве.

Схема алгоритма решения данной задачи представлена на рис. 3, а программа в лист. 2.

Решение поставленной задачи осуществляется с помощью цикла по параметру i, изменяющегося шагом +1 от 0 до m-1 с вложенным циклом по параметру j, изменяющемуся шагом +1 от 0 до n-1.

Перед входом в цикл переменной max присваивается значение у[0,0] элемента массива. С каждым изменением параметраj происходит переход к новому элементу той же строки массива и сравнение его значения со значением переменной max.

 

 
 

 

 


Рисунок 3– Схема алгоритма к примеру 2

Если значение элемента массива больше, то max присваивается значение элемента массива с текущим порядковым номером. Когда при данном значении параметра i завершается цикл по параметру j, происходит изменение параметра i и переход к новой строке двумерного массива.

Листинг2 Программа к примеру 2

usingSystem;

namespace ConsoleApplication1

{

classProgram

{

staticvoid Main(string[] args)

{

constint m = 3, n = 4;

int[,] y = newint[m, n] {

{ 2, 2, 8, 9 },

{ 4, 5, 6, 2 },

{ 7, 0, 1, 1 }

};

 

Console.WriteLine("Исходныймассив:");

for (inti = 0; i< m; ++i)

{

for (int j = 0; j < n; ++j)

Console.Write(" " + y[i, j]);

Console.WriteLine();

}

int max=y[0,0];

for (inti = 0; i< m; ++i)

{

for (int j = 0; j < n; ++j)

if (y[i, j] > max) max = y[i, j];

}

Console.WriteLine("Наибольший элемент в матрице "+ max);

}

}

}

Результаты работы программы:

 

Класс Random

Класс Random, определенный в пространстве имен System, содержит методы, позволяющие при отладке программ генерировать исходные данные, заданные случайным образом. Особенно это удобно при отладке программ обработки массивов данных, поскольку появляется возможность освободить программиста от утомительной работы ввода последовательности данных. Разумеется, подобный вариант может быть использован лишь тогда, когда ход решения задачи позволяет применять данные, полученные путем такой генерации.

ВС# для получения псевдослучайной последовательности чисел существует два варианта создания экземпляра класса Random: конструктор без параметрови конструктор с параметром типа int. Рассмотрим их.

Конструктор без параметров:

Randoma = newRandom( );

создает уникальную последовательность, так как использует начальное значение генератора, вычисленное на основе текущего времени.

Конструктор с параметром типа int:

Randomb = newRandom(5);

задает начальное значение генератора, что обеспечивает возможность получения одинаковых последовательностей чисел.

Для получения очередного значения серии пользуются методами, перечисленными в табл. 1.

Таблица 1– Основные методы класса System.Random

Название Описание
Next( ) Возвращает целое положительное число во всем положительном диапазоне типа int
Next(макс) Возвращает целое положительное число в диапазоне [0, макс]
Next(мин, макс) Возвращает целое число в диапазоне [мин, макс]
NextDouble() Возвращает вещественное положительное число в диапазоне [0. 1)
NextBytes(массив) Возвращает массив чисел в диапазоне [0, 255]

Первые четыре метода позволяют получить одно число. Для получения последовательностислучайных чисел необходима организация цикла. Последний метод табл. 1 генерирует массив чисел.

Посмотрим результаты использования методов данного класса, которые дадут представление об их работе (лист.3).

Листинг3– Работа с генератором псевдослучайных чисел

usingSystem;

namespaceConsoleApplication1

{ classProgram

{Random l = newRandom( );

Random d = newRandom( 1 );

constint n = 5;

Console.WriteLine("Числавдиапазоне [0, 1]" );

for (inti = 0; i< n; ++i)

Console.Write("{0,6:0.##}", l.NextDouble() );

Console.WriteLine();

Console.WriteLine("Числа в диапазоне [0, 100]");

for ( inti = 0; i< n; ++i )

Console.Write(" " + d.Next( 100 ) );

Console.WriteLine();

Console.WriteLine("Числа в диапазоне [-5, 5]");

for ( inti = 0; i< n; ++i )

Console.Write(" " + l.Next( -5, 5) );

Console.WriteLine();

Console.WriteLine("Массивчиселвдиапазоне[0, 255]");

byte[ ] mas = newbyte[n];

l.NextBytes(mas);

for (inti = 0; i< n; ++i)

Console.Write(" " + mas[i] );

}

}

}

Результат работы программы:

Ступенчатые массивы

В языкеС# можно создавать двумерный массив специального типа, который называется ступенчатыммассивом.

Рисунок 4– Ступенчатый массив

Ступенчатый или невыровненныймассив– это массив одномерных массивов, каждый из которых представляет собой строку. Количество элементов в разных строках может различаться. В памяти ступенчатый массив также хранится иначе, чем прямоугольный: в виде нескольких внутренних массивов, каждый из которых имеет свой размер. Крометого, выделяется отдельная область памяти для хранения ссылок на каждый из внутренних массивов. Организацию ступенчатого массива иллюстрируетрис. 4.

При объявлении ступенчатого массива каждая его размерность обозначается парой квадратных скобок:

тип[ ] [ ] имя;

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

int[][] b = newint [3][];

Не смотря на то, что такое объявление указывает количество строк в массиве, память для строк массива еще не выделена. Строки ступенчатого массива в памяти размещаются индивидуально, поэтомупод каждый из одномерных массивов, составляющих ступенчатый массив, память требуется выделять явным образом, например:

Выделение памяти под 0-ю строку(5 элементов):

b[0]=newint [5]; //

Выделение памяти под 1-ю строку (3 элемента):

b[1] =newint [3];

Выделение памяти под 2-ю строку (4 элемента):

b[2] = newint [4];

Здесь b[0], b[1] и b[2]– это отдельные массивы, к которым можно обращаться по имени, ниже приведен пример работы со ступенчатым массивом с использованием выделения памяти под каждую строку (лист. 3).

Другойспособвыделенияпамяти:

int[] [] b = {newint[5], newint[3], newint[4]};

K элементу ступенчатого массива обращаются, указывая каждую размерность в своих квадратных скобках, например:

b[1][2] b[i][j] b[j][i]

В остальном использование ступенчатых массивов не отличается от использования прямоугольных.

В листинге 4 продемонстрировано применение элементов класса Аrrау при работе со ступенчатым массивом.

Листинг4 - Использование методов класса Аrrау для обработки ступенчатого массива

using System;

namespace ConsoleApplication3

{

classProgram

{

staticvoid Main(string[] args)

{

int[][] b = newint[3][];

b[0] = newint[5] { 4, 5, 8, 3, 6 };

b[1] = newint[3] { 7, 9, 1 };

b[2] = newint[4] { 6, 5, 3, 1 };

 

Console.WriteLine("Исходныймассив:");

for (inti = 0; i<b.Length; ++i)

{

for (int j = 1; j <b[i].Length; ++j)

Console.Write(" " + b[i][j]);

Console.WriteLine();

}

Console.WriteLine(Array.IndexOf(b[0], 8));

 

}

}

}

Необходимо обратить внимание на то, как внутри цикла по строкам определяется длина каждого одномерного массива (длина каждой строки массива).

Результат работы программы:

Оператор цикла с перебором foreach

Цикл перебора foreach используется для просмотра всех объектов из некоторой группы данных – коллекции. В языкеС# определен ряд типов коллекций, например, коллекцией являются массивы.

Синтаксис записи цикла foreach:

foreach(тип имя_переменнойinимяколлекции) выражение;

где тип иимя_переменнойзадают тип и имя итерационной переменной, которая в процессе итераций циклаforeach получает значения элементов коллекции, заданной своим именем.

Если в программе в качестве коллекции используется массив данных, то необходимо помнить, что тип итерационной переменной должен совпадать или быть совместимым с базовым типом массива. Необходимо также учитывать, что итерационная переменная как элемент массива доступна только для чтения, и ей нельзя присвоить новое значение, а значит с помощью этой конструкции нельзя изменить и значение элемента массива.

Рассмотрим программу поиска максимального элемента в одномерном массиве.

Листинг5 - Использование операторацикла foreachдля поиска наибольшего элемента одномерного массива

using System;

namespace ConsoleApplication5

{

classProgram

{

staticvoid Main(string[] args)

{

constint n = 6;

int[] a = newint[n] { 3, 12, 5, -9, 8, -4 };

 

Console.WriteLine("Исходныймассив:");

for (inti = 0; i< n; ++i)

Console.WriteLine("\t" + a[i]);

Console.WriteLine();

int max = a[0];

foreach (int x in a)

{

if (x > max)

max = x;

}

Console.WriteLine("Максимальныйэлемент = " + max);

Console.Read();

}

}

}

Как видно из текста программы цикл foreachпоследовательно просматривает все элементы массива а и присваивает итерационной переменной х значение очередного элемента массива. Значение х в дальнейшем используется в оператореif для коррекции переменной max.

Результат работы программы:

Следует отметить тот факт, что не всегда требуется просматривать всю коллекцию до конца, например, в задаче, где требуется найти первый отрицательный элемент. После того как отрицательный элемент найден, можно прервать работу цикла с помощью оператора break.

При работе с многомерными массивами цикл foreach возвращает элементы в порядке следования строк.

Листинг6 - Использование операторацикла foreachдля поиска наибольшего элемента двумерного массива

using System;

namespace ConsoleApplication1

{

classProgram

{

staticvoid Main(string[] args)

{

constint m = 3, n = 4;

int[,] y = newint[m, n] {

{ 2, 2, 8, 9 },

{ 4, 5, 6, 2 },

{ 7, 0, 1, 1 }

};

 

Console.WriteLine("Исходныймассив:");

for (inti = 0; i< m; ++i)

{

for (int j = 0; j < n; ++j)

Console.Write(" " + y[i, j]);

Console.WriteLine();

}

int max = y[0, 0];

foreach (int x in y){

if (x > max) max = x;

}

Console.WriteLine("Наибольший элемент в матрице " + max);

Console.Read();

}

}

}

Как видно из текста программы использование одного оператора foreachпозволило заменить два вложенных оператора forв поиске наибольшего элемента двумерного массива.

Результат работы программы:

 

Массивы с числом размерностей больше двух

В данной главе были подробно рассмотрены основные аспекты работы с двумерными массивами. Параллельно было показаны отличия этих аспектов от тех, которые применимы к обработке одномерных массивов. Следует отметить, что потребности различных направлений инженерной и научной мыслей не исчерпываются использованием только рассмотренных типов массивов. Можно привести много практических и теоретических задач, предполагающих применение массивов более высоких размерностей.

ВС# разрешено использование массивов, имеющих более двух размерностей. Общая форма объявления многомерного массива:

тип[,…,] имя = new тип [ разм1,…размN];

Например, трехмерный массив может быть объявлен следующим образом:

int [,,] b= newint[5,4,3];

Обращение к элементу такого массива должно происходить с помощью переменной, рядом с которой в квадратных скобках через запятую указаны значения трех индексов:

b [2,1,2]

Многомерный массив можно инициализировать, заключив список инициализаторов для каждой размерности в фигурные скобки.

Для иллюстрации сказанного ниже приведена программа (лист. 7) поиска наибольшего элемента в трехмерном массиве.

Листинг 7 - Поиск наибольшего элемента в трехмерном массиве

using System;

namespace ConsoleApplication1

{

classProgram

{

staticvoid Main(string[] args)

{

constint m = 3, n = 4, c=2;

int[,,] y = newint[c,m, n] {{

{ 2, 2, 8, 9 },

{ 4, 5, 6, 2 },

{ 7, 0, 1, 1 }},

{

{ 1, 1, 0, 1 },

{ 4, 5, 6, 2 },

{ 7, 0, 1, 1 }}};

Console.WriteLine("Исходныймассив:");

for (int k = 0; k < c; ++k)

{

for (inti = 0; i< m; ++i)

{

for (int j = 0; j < n; ++j)

Console.Write(" " + y[k, i, j]);

Console.WriteLine();

}

}

int max = y[0, 0,0];

for (int k = 0; k < c; ++k)

{

for (inti = 0; i< m; ++i)

{

for (int j = 0; j < n; ++j)

if (y[k, i, j] > max) max = y[k, i, j];

}

}

Console.WriteLine("Наибольший элемент в матрице " + max);

}

}

}

 

В инициализирующей части объявления трехмерного массива упредставлены первая и вторая его страницы (размерность к), каждая из которых содержит mстрок и n столбцов.

Для перебора всех элементов массива потребовались три вложенных друг в друга цикла, каждый по своему измерению: вначале номер страницы, затем строки и, наконец, столбца.

 

Результаты работы программы: