Работа со ступенчатыми массивами

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

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

static void Main(string[] args)

{

float[][] b; //объявим неровный массив

int n, m;

Console.Write("Строк ");

n = Convert.ToInt32(Console.ReadLine());

b = new float[n][]; //определим количество строк

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

{

Console.Write("Элементов в строке"+i+" ");

m = Convert.ToInt32(Console.ReadLine());

b[i] = new float[m]; // определим количество

// элементов в i-ой строке

}

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

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

{

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

b[i][j] = Convert.ToSingle(Console.ReadLine());

}

float []dol=new float[n]; // массив для долей

// положительных элементов в строке

int kol;

float maxdol=0; //максимальная доля

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

{

kol=0;

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

if(b[i][j]>0)kol++;

if (b[i].Length!=0)

dol[i]=(float)kol/b[i].Length;

else

dol[i]=0;

if(maxdol<dol[i])maxdol=dol[i];

}

if (maxdol == 0)

Console.WriteLine

("Нет положительных элементов в массиве");

else

{

string s=""; //в эту переменную соберем номера строк

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

if (dol[i]==maxdol) s+=" "+i;

Console.WriteLine

("Максимальная доля в строках "+s);

}

Console.ReadLine();

}

Примечания.

  1. Если b неровный массив, то b.Length возвращает количество строк, а b[i].Length количество элементов в i–й строке.
  2. Нулевое количество элементов в строке не является ошибкой, в таком случае b[i].Length=0.
  3. присваивание string s=""; обязательно, даже для пустой строки.

Контрольные вопросы

1. Допустим, что в программе имеются объявления:

int i = 10, j = 9;

float x;

и оператор x = i / j; Какое знгачение получит х? Почему?

2. В каких случаях целесооюразно использовать ступенчатые массивы?

3. Когда целесообразно использовать оператор цикла foreach ?

4. К чему равно значение свойства массива Length (для одномерных,

двумерных и ступенчатых массивов)?

5. Какое значение возвращает функция GetUpperBound(i) в

зависимости от значения i ?

Работа с функциями

Общие принципы

В C#, как и в других языках, функции используют для двух целей:

- Для написания алгоритмов, которые должны выполняться многократно с разными исходными данными.

- Как средство структурирования программы: большие задачи целесообразно разделить на подзадачи, которые затем будут реализованы функциями.

В C# функция может иметь тип возвращаемого значения или не иметь его (иметь тип void). Формальные параметры-переменные могут быть переданы как по значению, так и по ссылке.

По умолчанию – по значению.

Для организации передачи по ссылке используют ключевые слова:

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

оutпозволяет получить в функции новое значение для параметра.

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

Проиллюстрируем сказанное несколькими примерами.

Пример 1.

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

void Nom1(out int k, int []mas)

{

int i;

k=-8;

for(i=0;i<mas.Length;i++)

if(mas[i]<0){k=i;break;}

}

Параметр k получает значение в результате выполнения функции.

Второй вариант:

int Nom2(int []mas)

{

int i;

k=-8;

for(i=0;i<mas.Length;i++)

if(mas[i]<0){k=i;break;}

return k;

}

Пример 2.

Найти номер первого элемента одномерного массива в заданном интервале после заданного элемента. При его отсутствии – вернуть в качестве ответа заданный номер.

void Number(ref int k,double c1,double c2,double[]mas)

{

for(int i=k;i<mas.Length;i++)

if (mas[i] > c1 && mas[i] < c2) { k = i; break; }

}

Параметр k должен иметь значение до обращения к функции (отсутствие значения – синтаксическая ошибка!) и может менять своё значение в результате выполнения функции. Параметры с1 и с2 передаются по значению и могут быть лишь исходными данными в функции.

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

2.2. Процедурное программирование в C#

Рассмотрим в этом параграфе компоновку программы с функциями. Вспомните структуру программы, описанной в § 1.1. Рассмотрим сначала, как можно использовать приведенные выше функции.

Функция из примера 1.

namespace ConApp3

{

class Program

{

static void Nom1(out int k, int[] mas)

{

int i;

k = -8;

for (i = 0; i < mas.Length; i++)

if (mas[i] < 0) { k = i; break; }

}

static void Main(string[] args)

{

int[] m ={5,9,2,6,7,56,-100};

int p;

Nom1(out p, m);

if (p < 0) Console.WriteLine("Нет отрицательных");

else

Console.WriteLine("Номер элемента "+p);

Console.ReadLine();

}

} }

Главная функция Main находится внутри класса Program и, поэтому имеет полное право работать его функциями с любым атрибутом доступа. Чтобы можно было вызывать функцию без создания экземпляра класса необходимо объявить функцию статической static. Обратите внимание на то, что и при вызове функции присутствует атрибут out.

Функция из примера 2.

namespace ConApp3

{

class Program

{

void Number(ref int k, double c1, double c2,

double[] mas)

{

for (int i = k; i < mas.Length; i++)

if (mas[i] > c1 && mas[i] < c2)

{ k = i; break; }

}

static void Main(string[] args)

{

double[] m ={5.1,9.4,2.7,6.8,7.1,56.8,100.0};

int q=2;

Program myclass = new Program();//1

myclass.Number(ref q, 8.0, 15.0, m); //2

Console.WriteLine("Номер элемента "+q);

Console.ReadLine();

} } }

В этом примере описатель static отсутствует, поэтому перед вызовом функции необходимо создать экземпляр класса (строка // 1). После этого вызовы всех функций из класса Program выполняют через квалификатор (строка // 2). Обратите внимание на то, что и при вызове функции присутствует атрибут ref.

В завершение параграфа приведем пример, содержащий функции:

- ввода массива,

- обработки массива – нахождение среднего арифметического.

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

namespace ConApp4

{

class Program

{

static int t1 = 0;

//объявление глобальной переменной для функций

static void Inpt(int[][] k)

{ //ввод массива

int n, m;

t1++; //работа с глобальной переменной

for (int i = 0; i < k.Length; i++)

for (int j = 0; j < k[i].Length; j++)

{

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

k[i][j] = Convert.ToInt32(Console.ReadLine());

}

}

static float Proc(int[][] d)

{ // обработка массива

float sr = 0;

int kol = 0;

t1+=5; //работа с глобальной переменной

for(int i=0;i<d.Length;i++)

for (int j = 0; j < d[i].Length; j++)

{

sr += d[i][j];

kol++;

}

return sr / kol;

}

static void Main(string[] args)

{

int[][] mas;

int n, m;

Console.Write("Строк ");

n = Convert.ToInt32(Console.ReadLine());

mas = new int[n][]; //определим количество строк

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

{

Console.Write("Элементов в строке " + i + " ");

m = Convert.ToInt32(Console.ReadLine());

mas[i] = new int[m]; // определим количество

// элементов в i-ой строке

}

Inpt(mas);

Console.WriteLine("Среднее арифметическое " +

Proc(mas)+

+” t1=”+t1); //работа с глобальной переменной

Console.ReadLine();

} } }

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

namespace ConApp6

{

class Program

{

static int[] fun1(int[] k)

{ // результат функции - массив

int kol = 0;

for (int i = 0; i < k.Length; i++)

if (k[i] > 0) kol++;

int []res=new int[kol]; //объявление результата - массива

kol = 0;

for (int i = 0; i < k.Length; i++)

if (k[i] > 0) res[kol++] = k[i];

return res; //возвращение массива в качестве результата

}

static void Main(string[] args)

{

int[] mas,arr; // mas исходный массив arr - результат

int n;

Console.Write("Count of Elements ");

n = Convert.ToInt32(Console.ReadLine());

mas = new int[n];

for (int i = 0; i < mas.Length; i++)

{

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

mas[i] = Convert.ToInt32(Console.ReadLine());

}

arr = fun1(mas); // инициализация массива - результата

// не требуется

for (int i = 0; i < arr.Length; i++)

Console.WriteLine("arr[" + i + "]="+arr[i]);

Console.ReadLine();

}

}

}

Контрольные вопросы

1. Для чего используют функции?

2. Для чего используют атрибуты out и ref?

3. Что означает атрибут static?

4. Для чего целесообразно использовать глобальные переменные?

3. Объектно-ориентированное программирование на C#

Общие принципы

Базовыми понятиями объектно-ориентированного программирования являются объект и класс. Объект – это какой-то реально существующий предмет со всеми его индивидуальными характеристиками. Класс – это множество объектов с одинаковыми характеристиками и одинаковым поведением. При определении значений характеристик класс превращается в объект. Характеристики класса задают данными, а поведение – методами. В C# методы представляют собой функции, среди методов выделяют конструктор и деструктор – функции особого назначения и с особыми правилами оформления.

Свойства объектно-ориентированного программирования:

- инкапсуляция (объединение в одной структуре данных – классе объявления данных и методов их обработки);

- наследование (класс может иметь одного предка, данные предка автоматически включаются в его состав, можно использовать методы предка);

- полиморфизм (можно иметь несколько реализаций одного метода с автоматическим выбором подходящего).

Рассмотрим в этой главе работу с классами в C#.

Наши рекомендации