И.Г. Головин, И.А. Волкова - Языки и методы программирования (1160773), страница 16
Текст из файла (страница 16)
Для выхода из тела цикла можно использовать операторbreak;.Рассмотрим пример цикла, игнорирующего пустые строки входнойпоследовательности:for (;;){string s = Console.ReadLine();if (s == null) break;if (s.Length != 0) Console.WriteLine (s);}Здесь выход из цикла происходит, если закончилась входная последовательность.Кроме break; есть еще один оператор, нарушающий последовательный порядок выполнения — continue;. Этот оператор игнорирует остаток тела и сразу переходит на проверку условия (приэтом выражение еЗ все равно выполняется).
Операторы break;и continue; могут появляться и в телах других операторов цикла.В языках C# и Java есть еще одна форма оператора for — циклforeach, который в C# имеет видforeach (Т v in Coll) SЗдесь T v — это объявление переменной, a Coll — объектконтейнер (например, массив). Тип данных Т должен быть типомэлементов контейнера. Переменная v последовательно принимаетзначение элементов контейнера. Например, суммирование элементовмассива double [ ] arr можно выполнить следующим образом:73double sum = 0;foreach (double x in arr) sum += x;Такая форма не использует операцию индексирования, поэтомуболее эффективна, чем пример из подразд.
5.2. Правда, оператор цикла foreach не может использоваться для модификации контейнераи его элементов. В таких случаях надо использовать другие циклы.В языке Java синтаксис цикла foreach немного отличается, носемантика остается такой же:for (Т v: Coll) SТрансляторы C# и Java определяют понятие «контейнер» (в этихязыках контейнеры носят название коллекция) как класс, реализующий специальный интерфейс (в C# — IEnumerable, а в Java —Iterable).
Понятие интерфейса будет рассмотрено в подразд.8.3, а здесь отметим лишь, что этот интерфейс реализуют массивыи многие классы-коллекции из стандартной библиотеки (например,ArrayList). Важно, что программисты могут создавать свои классыколлекции, которые будут эффективно обрабатываться операторомцикла foreach.Глава 6ПРОЦЕДУРНЫЕ АБСТРАКЦИИ6.1. Подпрограммы. Передача управленияв подпрограммахПодпрограмма — это абстракция последовательности команд.Мы можем дать некоторой последовательности операторов имяи использовать это имя в программе вместо указания всей последовательности.Подпрограммы подразделяются на процедуры и функции.Основное назначение функции — вычисление одного значения.Таким образом, функция — это обобщение понятия «операция»и мощное средство для расширения базисного набора операций.Для указания того, какое значение вычисляет функция, используетсяоператор явного возврата значения:return выражение;Значение выражения и будет значением функции.Целью процедуры является исключительно побочный эффект —изменение состояния выполняемой программы (например, изменение значений переменных, ввод-вывод и т.
п). Таким образом,процедура — это обобщение понятия «оператор» и средство длярасширения базисного набора операторов.Подпрограммы — это первое средство развития, которое появилось в языках программирования. Понятие подпрограммы оченьхорошо согласуется с методологией структурного программирования.В соответствии с этой методологией программа должна строиться изструктурных блоков, имеющих одну точку входа и одну точку выхода.Проектирование программы начинается с больших блоков (например, три блока «подготовить — обработать — завершить» хорошоподходят под многие задачи обработки данных). Далее каждый блокдетализируется в виде суперпозиции более подробных блоков, покане доходят до уровня операторов языка.
Средствами суперпозициимогут служить только управляющие конструкции язы ка (именнопоэтому нельзя использовать оператор перехода). Если оформлятькаждый структурный блок (или хотя бы наиболее крупные блоки)в виде подпрограмм, то решение задачи значительно проще понятьи разработать.С современной точки зрения подпрограмма — это «черный ящик»,имеющий один вход и один выход. Программисту, использующему75подпрограмму, неважно какова структура этого ящика так же, какневажно из какой точки подпрограммы осуществляется возврат.Понятие подпрограммы подразумевает наличие двух конструкций:• определение подпрограммы;• вызов подпрограммы.К сожалению, терминология языка С, перешедшая в C++, Javaи С#, не совпадает с традиционной.
В языке С все подпрограммыназываются функциями. Процедура реализуется в С как функция,у которой нет возвращаемого значения. В процедуре нельзя использовать оператор явного возврата значения. Если требуется выйтииз середины процедуры, то можно использовать оператор возвратав формеreturn;В обычных функциях использовать эту форму оператора returnнельзя.Определение функции имеет видтип имя(список_формальных_параметров)телоТип — это тип возвращаемого значения. Для процедур используется специальное ключевое слово — void, сигнализирующее оботсутствии возвращаемого значения. В списке формальных параметров специфицируются имена и типы параметров (другое названиепараметра — аргумент).
Тело — это блок, выполняемый при вызовефункции.Пример определения функции:int GCD (int a, int b){while (a != b){if (a < b)b -= a;elsea -= b;}return a;}Пример определения процедуры (C++):void swap(T& x, T& y){T temp = x;x = у; у = temp;}76Вызов функции имеет видимя (список_фактических_параметров)Вызов процедуры может быть только оператором-выражением:swap(а,Ь);Вызов функции, возвращающей значение, может появиться везде,где может стоять значение соответствующего типа:х = G C D (а,Ь);cout << "Наибольший общий делитель 980 и 24 = "« GCD (980, 24);При вызове функции выполнение начинается с первого операторатела функции и завершается выполнением оператора возврата.
В процедурах оператор возврата может отсутствовать, тогда выполнениезавершается по выходу из блока тела процедуры.6.2. Передача параметров в подпрограммахРассмотрим вопрос параметризации подпрограмм. Для каждойподпрограммы указывается набор формальных параметров.Можно рассматривать формальные параметры как локальныепеременные тела подпрограммы.При вызове подпрограммы указывается список фактическихпараметров. Соответствие между фактическими и формальными параметрами выполняется по позиции в списке: первый фактическийпараметр соответствует первому формальному параметру и т.д.
Такой способ называется позиционным. Язык С#, начиная с версии 4,предусматривает альтернативный — ключевой способ отождествления, в котором используются имена формальных параметров, номы не будем его рассматривать. Очевидно, что при позиционномспособе отождествления количество формальных параметров должносовпадать с количеством фактических.Фактические параметры представляют собой выражения или переменные (частные случаи выражения).Рассмотрим подробнее связывание фактических и формальныхпараметров.
Это связывание всегда динамическое, так как происходитв момент вызова подпрограммы.Существует три вида формальных параметров:• входные параметры (параметры, от которых требуется только значение). Мы используем только значения фактических параметров,которые не меняются при выходе из тела функции;• выходные параметры (эти параметры не обязаны иметь начальноезначение, но могут быть изменены в теле функции);77• изменяемые параметры (требуется и исходное значение, и возможность его изменения).С входным параметром может связываться произвольное выражение, а выходным или»изменяемым — только объекты, которые могутстоять в левой части оператора присваивания.В большинстве языков программирования вместо указания видапараметра указывается способ (механизм) связывания параметра,называемый способом передачи параметра.Существует два основных способа передачи параметров: по значению и по ссылке.Передача параметров по значению.
Формальный параметресть некоторая локальная переменная. Место для локальныхпеременных отводится в стеке. При вызове подпрограммы значение фактического параметра копируется в соответствующийформальный параметр. Все изменения формального параметрасвязаны с изменением локальной переменной и не сказываютсяна фактическом параметре.
Перед копированием может потребоваться приведение типа, если типы фактического и формальногопараметров не совпадают.Передача параметров по ссылке. Фактически этот способ естьпередача ссылки по значению. Формальный параметр — это ссылка на объект. В момент вызова происходит инициализация ссылкифактическим параметром. Преобразования типов в этот момент непроисходит: типы формального и фактического параметров должнысовпадать. Поскольку ссылка после инициализации отождествляетсяс объектом, то любые изменения формального параметра подразумевают изменения фактического параметра.Очевидно, что способ передачи по значению соответствует семантике входных формальных параметров.По ссылке можно передавать выходные и изменяемые параметры.В языке С существует только способ передачи параметров позначению (ссылочный тип в языке С отсутствует).
Если требуетсявыходной или изменяемый параметр, то программист должен передавать адреса, тем самым «вручную» моделируя передачу параметрапо ссылке. Например, процедуру swap из предыдущего пункта можнопереписать на С и C++ следующим образом:void swap(T* х, Т* у){Т temp = *х;*х = *у; *у = temp;}Вызов этой процедуры имеет видТ а,Ь;swap(&а,&Ь);78В языке С это единственный вариант реализации семантики изменяемых параметров, но в языке C++, безусловно, предпочтителенвариант передачи параметров по ссылке.В языке Java, как и в С, параметры передаются только по значению. При этом указателей и операции взятии адреса в языке нет,поэтому написать аналог процедуры swap нельзя.
Однако в этомязыке есть референциальные типы, поэтому объявление параметра референциального типа и означает передачу самого объекта поссылке, а выходные и изменяемые параметры типов-значений использовать в Java нельзя.В языке C# объекты любых типов можно передавать как по значению, так и по ссылке. Для реализации выходных и изменяемыхпараметров существуют ключевые слова ref и out.По умолчанию формальные параметры передаются по значению.Однако перед выходным формальным параметром в списке должностоять ключевое слово out, а перед изменяемым — ref. Например:void swap(ref int x, ref int y){int tmp = x; x = у; у = tmp;}void genarray(out b yte[] a, int len){a = new byte[n];}Вызовы при этом имеют видint а = 0; b = -1;swap(ref a, ref b );byte [] array;genarray(out array, 256);Обратите внимание на наличие слов ref и out как в объявлениифункций, так и в вызове.Способы передачи параметров касаются механизма реализациисвязывания, но не семантики входных и выходных параметров.
Понятно, что семантике входных параметров соответствует передача позначению, но при этом способе происходит копирование содержимого фактического параметра, что неприемлемо для значений большогоразмера. Их лучше передавать по ссылке.Приведем пример программы передачи параметров на языкеC++struct Large {char body[100000] ;...
// другие поля};79void BadProc(Large s); // не лучший способ!!!void BetterProc(LargeS s); // уже лучшеОднако передача по ссылке позволяет менять фактический параметр произвольным образом. Семантика входного параметра приэтом теряется. Для того чтобы сохранить (и проконтролировать) неизменяемость ссылочных параметров, в языке C++ можно использоватьссылку на константу:void BestProc(const LargeS s) ;Теперь при попытке изменить содержимое формального параметраs компилятор C++ выдаст сообщение об ошибке.Перегрузка именПерегрузка имени означает, что в одной области видимости имеется несколько определяющих вхождений одного и того же имени.В языках программирования определяющим вхождениям обычносоответствуют объявления имен.
В этом случае перегрузка имениозначает, что в одной области видимости находится несколько разныхобъявлений одного и того же имени.Обычно такая ситуация расценивается как ошибка. В самомделе, каждое объявление вводит некоторую сущность (например,объект данных, тип данных и т.п.). Иметь разные сущности, названные одним именем, неудобно, а иногда и невозможно, если ихнельзя различить по контексту. Однако это не относится к именамподпрограмм. Имена подпрограмм в большинстве индустриальныхязыков программирования можно перегружать.