Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Языки программирования. Практический сравнитель...doc
Скачиваний:
31
Добавлен:
09.09.2019
Размер:
2.68 Mб
Скачать

6.8. Упражнения

1. Реализует ваш компилятор все case-/switch-операторы одинаково, или он пытается выбирать оптимальную реализацию для каждого оператора?

2. Смоделируйте оператор repeat языка Pascal в Ada и С.

3. Первоначально в языке Fortran было определено, что цикл выполняет­ся, по крайней мере, один раз, даже если значение low больше, чем зна­чение high! Чем могло быть мотивировано такое решение?

4. Последовательный поиск в языке С:

C

while (s[i].data != key)

i++;

можно записать как

C

while (s[i++].data != key)

; /* Пустой оператор */

В чем различие между двумя вариантами вычислений?

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

6. Сравните сгенерированный для поиска код, реализованный с помощью операторов break или exit, с кодом, сгенерированным для поиска с «часовым».

7. Напишите программу поиска с «часовым», используя do-while вместо while. Будет ли это эффективнее?

8. Почему мы помещали «часового» в начало массива, а не в конец?

9. (Шолтен) В игре Го используют камни двух цветов, черные и белые. Предположим, что у вас в коробке неизвестная смесь камней, и вы вы­полняете следующий алгоритм:

while Stones_Left_in_Can loop -- пока есть камни в коробке

Ada

Remove_Two_Stones(S1, S2); -- вынуть два камня

if Color(S1 )=Color(S2) then

Add_Black_Stone; --добавить черный камень

else

Add_White_Stone; -- добавить белый камень

end if;

end loop;

Найдите переменную, значение которой уменьшается, оставаясь неотрицательным, и тем самым покажите, что цикл заканчивается. Мо­жете ли вы что-нибудь сказать относительно цвета последнего камня? (Подсказка: напишите инвариант цикла для числа белых камней).

Глава 7

Подпрограммы

7.1. Подпрограммы: процедуры и функции

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

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

• Подпрограмма — это логическая единица декомпозиции программы. Да­же если сегмент выполняется только один раз, полезно оформить его в виде подпрограммы с целью тестирования, документирования и улучше­ния читаемости программы.

• Подпрограмму также можно использовать как физическую единицу де­композиции программы, т. е. как единицу компиляции. В языке Fortran подпрограмма (subroutine) — это единственная единица и декомпозиции, и компиляции. В современных языках физической единицей декомпозиции является модуль, представляющий собой группу объявлений и подпрограмм (см. гл. 13).

Подпрограмма состоит из:

• объявления, которое задает интерфейс с подпрограммой; это объявление включает имя подпрограммы, список параметров (если есть) и тип воз­вращаемого значения (если есть);

• локальных объявлений, которые действуют только внутри тела подпро­граммы;

• последовательности выполняемых операторов.

Локальные объявления и выполняемые операторы образуют тело под­программы.

Подпрограммы, которые возвращают значение, называются функциями (functions), а те, что не возвращают, — процедурами (procedures). Язык С не име­ет отдельного синтаксиса для процедур; вместо этого следует написать функ­цию, которая возвращает тип void, т.е. тип без значения:

C


void proc(int a, float b);

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

Обращение к процедуре задается оператором вызова процедуры call. В языке Fortran он имеет специальный синтаксис:

C


call proc(x,y)

тогда как в других языках просто пишется имя процедуры с фактическими па­раметрами:

C

ргос(х.у);

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

Так как функция возвращает значение, объявление функции должно опре­делять тип возвращаемого значения. В языке С тип функции задается в объяв­лении функции перед ее именем:

C


int func(int a, float b);

тогда как в языке Ada используется другой синтаксис:

Ada


function Func(A: Integer; В: Float) return Integer;

Вызов функции является не оператором, а элементом выражения:

C

a = x + func(r,s) + y;

Тип результата функции не должен противоречить типу, ожидаемому в выра­жении. Обратите внимание, что в языке С во многих случаях делаются неяв­ные преобразования типов, тогда как в Ada тип результата должен точно соот­ветствовать контексту. По смыслу вызов функции аналогичен вызову проце­дуры: приостанавливается вычисление выражения; выполняются команды тела функции; затем возвращенное значение используется для продолжения вычисления выражения.

Термин «функция» фактически совершенно не соответствует тому контек­сту, в котором он употребляется в обычных языках программирования. В математике функция — всего лишь отображение одного набора значений на дру­гой. Если использовать техническую терминологию, то математическая фун­кция не имеет побочного эффекта, потому что ее «вычисление» прозрачно в точке, в которой делается «вызов». Если есть значение 3.6, и вы запрашива­ете значение sin(3.6), то вы будете получать один и тот же результат всякий раз, когда в уравнении встретится эта функция. В программировании функ­ция может выполнять произвольное вычисление, включая ввод-вывод или изменение глобальных структур данных:

int x,y,z;

C

intfunc(void)

{

у = get(); /* Изменяет глобальную переменную */

return x*y; /* Значение зависит от глобальной переменной */

z = х + func(void) + у;

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

Поскольку все подпрограммы в С — функции, в программировании на языке С широко используются возвращаемые значения и в «невычисли­тельных» случаях, например в подпрограммах ввода-вывода. Это допустимо при условии, что понятны возможные трудности, связанные с зависимостью от порядка и оптимизацией. Исследование языков программирования приве­ло к разработке интереснейших языков, которые основаны на математически правильном понятии функции (см. гл. 16).