Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Комплект Информатика / Курс лекций.doc
Скачиваний:
128
Добавлен:
22.05.2015
Размер:
4.8 Mб
Скачать

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

1. Кратко охарактеризуйте поколения языков программирования.

2. Как понимается термин «парадигмы языков программирования»?

3. Что положено в основу классификации языков программирования?

4. Проведите краткий обзор языков программирования высокого уровня краткую характеристику.

5. Проведите краткий обзор языков программирования баз данных.

6. Проведите краткий обзор языков программирования для Интернета.

Лекция № 20 Процедурные единицы и реализация языка программирования

Цель лекции

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

План лекции

1. Процедурные единицы.

2. Реализация языка программирования.

1 Процедурные единицы

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

Процедура (procedure) — это набор команд для выполнения некоторой задачи, который другие программные единицы могут использовать в качестве абстрактного инструмента. Управление передается процедуре (с помощью команды перехода машинного языка), когда ее действия необходимы, а затем, после завершения выполнения процедуры, снова возвращается исходной программной единице (рис. 1). Процесс передачи управления процедуре называется вызовом процедуры. Программную единицу, которая запрашивает выполнение процедуры, мы будем называть вызывающей программой или вызывающим модулем (calling unit).

Рисунок 1 – Передачи управления при вызове процедуры

Во многих отношениях процедура представляет собой небольшую программу, состоящую из операторов описания, за которыми следуют исполняемые операторы, определяющие выполняемые процедурой действия. Как правило, переменная, объявленная в процедуре, является локальной переменной (local variable), то есть ее можно использовать только в данной процедуре. Такой подход исключает путаницу, которая может возникнуть, если две независимые друг от друга процедуры используют переменные с одинаковыми именами. Переменные, действие которых не ограничивается какой-либо одной частью программы, называются глобальными переменными (global variable), они доступны в любом месте программы. В большинстве языков программирования используются и локальные, и глобальные переменные.

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

Однако в отличие от нашего нестрогого псевдокода, в котором мы запрашивали выполнение процедуры с помощью такого выражения, как «Применить процедуру Deacti vateCrypton», большинство современных языков программирования позволяют вызывать процедуру, просто указав ее имя. Например, если GetNames, SortNames и WriteNames являются именами процедур для получения, сортировки и вывода на печать списка имен, то программу, получающую список, сортирующую его и выводящую на печать, можно записать как

GetNames:

SortNames;

WriteNames;

а не

Применить процедуру GetNames.

Применить процедуру SortNames.

Применить процедуру WriteNames.

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

Событийно-управляемые программные системы. Ранее мы рассматривали случаи, когда процедура активизируется командой, которая вызывает процедуру явным образом. Однако существуют случаи, когда процедура активизируется неявно при появлении какого-либо события. В качестве примера можно привести графические пользовательские интерфейсы, в которых процедура, описывающая, что должно произойти, если щелкнуть мышью на кнопке, активизируется не вызывающей программной единицей, а щелчком на кнопке. Системы программного обеспечения, в которых процедуры активизируются таким способом, называются событийно-управляемыми (event-driven system). Говоря проще, событийно-управляемые программные системы состоят из процедур, которые описывают, что должно происходить в результате различных событий. Во время выполнения системы эти процедуры находятся в бездействии до тех пор, пока не произойдет событие, активизирующее их, выполняют свою задачу и возвращаются в состояние покоя.

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

Такие абстрактные имена в процедурах называются параметрами (parameters). Точнее, имена, которые используются при написании процедуры, называются формальными параметрами (formal parameter), а конкретные значения, которые эти формальные параметры получают при вызове и используют при выполнении процедуры, называются фактическими параметрами (actual parameter). В некотором смысле формальные параметры представляют собой разъемы, в которые при вызове процедуры помещаются фактические параметры. На самом деле формальные параметры — это переменные, которым во время выполнения процедуры присваиваются необходимые значения (фактические параметры).

В языках программирования для задания формальных параметров применяется такой же метод, как и в нашем псевдокоде. То есть в большинстве языков программирования формальные параметры перечисляются в скобках в заголовке процедуры. Например, описание процедуры ProjectPopulation на языке Си приведено на рис. 2. Эта процедура при вызове получает значение темпа роста популяции и вычисляет предполагаемую численность популяции вида на ближайшие десять лет, предполагая, что исходная численность равна 100, а затем сохраняет полученные значения в глобальном массиве Population.

Рисунок 2 – Процедура ProjectPopulation, записанная на языке С

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

ProjectPopulation (0.03):

может использоваться в программе, написанной на языке С, для вызова процедуры ProjectPopulation (см. рис. 2) с присвоением параметру GrowthRate значения 0.03.

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

Предположим, что процедура PrintCheck определяется таким заголовком

procedure PrintCheck (Payee, Amount)

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

PrintCheck ("John Doe", 150)

формальному параметру Payee ставится в соответствие фактический параметр John Doe, а формальному параметру Amount — значение 150. Однако если вызвать ту же процедуру с помощью выражения

PrintCheck (150, "John Doe")

то значение 150 будет присвоено формальному параметру Payee, а имя "John Doe" — параметру Amount, что приведет к ошибкам в работе программы.

Передача данных между фактическими и формальными параметрами реализуется в языках программирования по-разному. В некоторых языках данные, представленные фактическими параметрами, копируются, а затем передаются процедуре. При таком подходе любые изменения данных, производимые процедурой, отражаются только на копии, данные же вызывающей программы остаются неизменными. Про такие параметры говорят, что они передаются по значению (passed by value). Обратите внимание на то, что передача параметров по значению защищает данные от неправильного их изменения плохо написанной процедурой. Например, если вызывающая программа передала имя сотрудника компании, то она вряд ли захочет, чтобы процедура изменила это имя.

Рисунок 3 - Выполнение процедуры Demo с передачей параметров по значению: а – при вызове процедуры ей передаются копии данных; б – процедура манипулирует копией данных; в – после выполнения процедуры вызывающая среда остается неизменной

К сожалению, передача параметров по значению неэффективна, когда параметры представляют собой большие совокупности данных. В таком случае лучше предоставить процедуре прямой доступ к фактическим параметрам, указав их адрес в вызывающей программной единице. О таких параметрах говорят, что они передаются по ссылке (passed by reference). Обратите внимание на то, что передача параметров по ссылке позволяет процедуре изменять данные, хранящиеся в вызывающей среде. Такого подхода желательно придерживаться в случае процедуры, сортирующей список, поскольку такая процедура и вызывается для того, чтобы изменить список.

Предположим, что процедура Demo была описана следующим образом:

procedure Demo (Formal)

Formal — Formal + 1;

Предположим также, что переменной Actual было присвоено значение 5. Мы вызываем процедуру с помощью выражения Demo (Actual).

Тогда, если параметры передаются по значению, то замена переменной на Formal никак не отразится на переменной Actual (рис. 3). Если же параметры передаются по ссылке, то значение переменной Actual увеличится на 1 (рис. 4).

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

Функции. Рассмотрим вариации понятия процедуры, которые существуют во многих языках программирования'. Иногда Цель лекции процедуры заключается в том, чтобы вычислить значение, а не выполнить какие-либо действия. (Рассмотрим различие между процедурой, которая подсчитывает число проданных вещей, и процедурой, которая сортирует список: в первом случае процедура возвращает значение, во втором — выполняет действие.) Процедура, возвращающая значение, называется функцией. Здесь термин «функция» применяется к программной единице, отличающейся от процедуры тем, что в результате ее выполнения значение вычисляется и возвращается вызывающей программе как «значение функции». Это значение можно либо сохранить в переменной, либо тут же использовать в вычислениях. Например, в языках С, C++ и С# можно использовать выражение

ProjectJanSales = EstimatedSales (January);

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

if (LastJanSales < EstimatedSales (January))

else ...

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

Функции определяются почти так же, как процедуры. Различие состоит в том, что заголовок функции обычно начинается с описания типа возвращаемого этой функцией значения, а в теле функции присутствует оператор возврата, операндом которого является возвращаемое значение. Определение функции CylinderVoiume в языке С приведено на рис. 5. (На самом деле существует более лаконичная форма записи, но из педагогических соображений мы используем эту запись). При вызове эта процедура получает конкретные значения для формальных параметров Radius и Height и возвращает результат вычисления объема цилиндра с такими параметрами. Таким образом, эту функцию можно использовать в программе для определения стоимости содержимого цилиндра радиусом 3.45 и высотой 12.7, поместив ее в оператор:

Cost = CostPerVolUnit * Cylinder-Volume (3.45. 12.7).

Рисунок 4 – Функция Cylinder-Volume, написанная на языке С

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

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

readln (Value);

а чтобы вывести это значение на экран

writeln (Value);

Обратите внимание на то, что синтаксис в этих примерах такой же, как при вызове процедуры с параметрами. Зарезервированные слова readln и writeln представляют собой имена процедур, написанных заранее для выполнения определенных действий.

Точно так же программист, работающий с языком С, для осуществления ввода и вывода информации может использовать процедуры (формально согласно терминологии, применяемой для языка С, они являются функциями) scanf и printf. Однако процедуры prinft языка С и writeln языка Pascal существенно отличаются в использовании параметров. Процедура writeln просто выводит переданные ей параметры в заданном порядке. В процедуре же prinft предполагается, что первый параметр описывает, в каком виде следует выводить следующие за ним параметры. То есть первый параметр в операторе вызова процедуры описывает, как данные будут располагаться на экране. Такой подход называется форматируемым вводом-выводом (formatted I/O).

Например, программист, чтобы вывести на экран переменные Значение1 и Значение2 в десятичном представлении на одной строке и затем перейти на новую строку, на языке Си напишет:

printf ("%d %d\n", Значение]., Значение2);

Обратите внимание на то, что первый параметр (в двойных кавычках) описывает, как текст будет отображаться на экране. Он состоит из директив преобразования (%d обозначает, что выводимое целое число необходимо преобразовать в строку десятичных цифр в кодировке ASCII и поместить ее на место соответствующей директивы), печатных символов (между двумя директивами %й расположен символ пробела) и непечатных символов или управляющих последовательностей (у нас это \n — перейти на новую строку). Следовательно, в нашем примере текст состоит из числа в десятичном представлении, за которым следует пробел, другое число, возврат каретки1 и перевод строки. Оставшиеся параметры задают переменные в порядке их вывода на экран. Функция prinft формирует результирующую строку с учетом изложенного выше и выводит ее на экран начиная с текущей позиции вывода.

Вот другой пример. Если переменным Agel и Аде2 присвоены значения 16 и 25 соответственно, то в результате выполнения оператора

printf ("Возраст участников от %б\п до ld.\n". Agel. Age2);

на экране появится сообщение

Возраст участников от 16 до 25.

Обратите внимание на то, что сообщение на экране получается вследствие помещения значений переменных Agel и Аgе2 в позиции, отмеченные значком %й, в первом параметре (рис. 6). Без форматируемого ввода-вывода программист, работающий на языке Pascal, для получения такого же результата использовал бы последовательность из двух команд

writeln ("Возраст участников от ". Agel);

writeln ("до". Age2. "."):

Поскольку языки C++, Java и С# являются объектно-ориентированными, операции ввода-вывода в них рассматриваются как передача данных к объекту и от объекта. В частности, язык C++ предоставляет уже готовые объекты cin и cout для обозначения стандартных устройств ввода (например, клавиатура) и вывода (например, монитор).

Рисунок 5 – Форматируемый вывод

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

cin » Value;

которое предписывает объекту cin присвоить полученные им данные переменной Value. Точно так же выражение

cout « Value:

предписывает объекту cout вывести на экран данные, присвоенные переменной

Value.