Добавить в цитаты Настройки чтения

Страница 277 из 371



Счетчики цикла зачастую объявляются непосредственно в инициализаторе и соответственно являются переменными, локализованными в цикле, так что после завершения цикла они перестают существовать.

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

В качестве примера рассмотрим классическую задачу: является ли строка текста палиндромом.

Напомню, палиндромом называется симметричная строка текста, читающаяся одинаково слева направо и справа налево. Для ее решения цикл for подходит наилучшим образом: здесь используются два счетчика — один возрастающий, другой убывающий. Вот текст соответствующей процедуры:

/// <summary>

/// Определение палиндромов. Демонстрация цикла for

/// </summary>

/// <param name="str">текст</param>

/// <returns>true — если текст является палиндромом</returns>

public bool Palindrom(string str)

{

     for (int i = 0,j =str.Length-1; i<j; i + +,j-)

         if(str [i]!=str [j]) return(false);

     return(true);

}//Palindrom

Циклы While

Цикл while (выражение) является универсальным видом цикла, включаемым во все языки программирования. Тело цикла выполняется до тех пор, пока остается истинным выражение while, в языке C# у этого вида цикла две модификации — с проверкой условия в начале и в конце цикла. Первая модификация имеет следующий синтаксис:

while(выражение) оператор

Эта модификация соответствует стратегии: "сначала проверь, а потом делай". В результате проверки может оказаться, что и делать ничего не нужно. Тело такого цикла может ни разу не выполняться. Конечно же, возможно и зацикливание. В нормальной ситуации каждое выполнение тела цикла — это очередной шаг к завершению цикла.

Цикл, проверяющий условие завершения в конце, соответствует стратегии: "сначала делай, а потом проверь". Тело такого цикла выполняется, по меньшей мере, один раз. Вот синтаксис этой модификации:

do

   оператор

while(выражение);

Приведу пример, в котором участвуют обе модификации цикла while. Во внешнем цикле проверка выполняется в конце, во внутреннем — в начале. Внешний цикл представляет собой типичный образец организации учебных программ, когда в диалоге с пользователем многократно решается некоторая задача. На каждом шаге пользователь вводит новые данные, решает задачу и анализирует полученные данные. В его власти, продолжить вычисления или нет, но хотя бы один раз решить задачу ему приходится. Внутренний цикл do while используется для решения уже известной задачи с палиндромами. Вот текст соответствующей процедуры:

/// <summary>

/// Два цикла: с проверкой в конце и в начале.

/// Внешний цикл — образец многократно решаемой задачи.

/// Завершение цикла определяется в диалоге

/// с пользователем.

/// </summary>

public void Loop()

{

     string answer, text;

     do

     {

         Console.WriteLine("Введите слово");

         text = Console.ReadLine();

         int i =0, j = text.Length-1;

         while ((i<j) && (text[i] == text[j]))

             {i + +; j-; }

         if (text[i] == text[j])

             Console.WriteLine (text +" — это палиндром!");





         else

             Console.WriteLine(text +" — это не палиндром!");

         Console.WriteLine("Продолжим? (yes/по)");

         answer = Console.ReadLine();

      }

      while (answer =="yes");

}//Loop

Цикл foreach

Новым видом цикла, не унаследованным от C++, является цикл foreach, удобный при работе с массивами, коллекциями и другими подобными контейнерами данных. Его синтаксис:

foreach (тип идентификатор in контейнер) оператор

Цикл работает в полном соответствии со своим названием — тело цикла выполняется для каждого элемента в контейнере. Тип идентификатора должен быть согласован с типом элементов, хранящихся в контейнере данных. Предполагается также, что элементы контейнера (массива, коллекции) упорядочены. На каждом шаге цикла идентификатор, задающий текущий элемент контейнера, получает значение очередного элемента в соответствии с порядком, установленным на элементах контейнера. С этим текущим элементом и выполняется тело цикла — выполняется столько раз, сколько элементов находится в контейнере. Цикл заканчивается, когда полностью перебраны все элементы контейнера.

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

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

/// <summary>

/// Демонстрация цикла foreach. Вычисление суммы,

/// максимального и минимального элементов

/// трехмерного массива, заполненного случайными числами.

/// </summary>

public void SumMinMax()

{

       int [,,] arr3d = new int[10,10, 10];

       Random rnd = new Random();

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

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

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

                 arr3d[i,j,k]= rnd.Next(100);

       long sum =0; int min=arr3d[0,0,0], max=arr3d[0,0,0];

       foreach (int item in arr3d)

       {

           sum +=item;

           if (item > max) max = item;

           else if (item < min) min = item;

       }

       Console.WriteLine("sum = {0}, min = {1}, max = {2}", sum, min, max);

}//SumMinMax

9. Процедуры и функции — методы класса

Процедуры и функции — две формы функционального модуля. Чем отличаются эти формы? Процедуры и функции — это методы класса. Описание методов (процедур и функций). Синтаксис. Атрибуты доступа. Статические и динамические методы. Формальные аргументы. Статус аргументов. Тело методов. Вызов процедур и функций. Фактические аргументы. Семантика вызова. Поля класса или аргументы метода? Поля класса или функции без аргументов? Проектирование класса Account. Функции с побочным эффектом. Перегрузка методов.

Процедуры и функции — функциональные модули

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

Процедуры и функции — методы класса

Долгое время процедуры и функции играли не только функциональную, но и архитектурную роль. Весьма популярным при построении программных систем был метод функциональной декомпозиции "сверху вниз", и сегодня еще играющий важную роль. Но с появлением ООП архитектурная роль функциональных модулей отошла на второй план. Для ОО-языков, к которым относится и язык С#, в роли архитектурного модуля выступает класс. Программная система строится из модулей, роль которых играют классы, но каждый из этих модулей имеют содержательную начинку, задавая некоторую абстракцию данных.