Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Процедуры и функции как параметры.
Существует класс задач, где исходными данными (аргументами) для программ (подпрограмм) являются не переменные и константы, а функции и процедуры. К таким задачам относятся например задача создания неких универсальных программ типа, например, универсальной программы построения графика нескольких функций, определенных на разных (своих) интервалах. Вид такой программы не должен зависеть от вида функции, используемой для вычисления значения на интервале. Если бы такую универсальную программу построения графика делать без передачи функции как параметра, то необходимо было бы сделать две вещи: 1). Распознавать, для какой конкретно функции (интервала) надо строить график; 2). Для каждого конкретного вида функции надо было бы использовать свой специфический вызов этой функции для определения положения и вывода точки на графике. Если в функцию построения графика передавать как параметр функцию, вычисляющую значение на интервале, то в общем виде заголовок этой функции (построения графика) можно было бы записать так: функция f как формальный параметр Procedure график(х: real; function f(x: real)): real; (так писать нельзя) Костяк универсальной процедуры построения графика может иметь следующий вид: function f1 (...): real; begin ... end; function f2 (...): real; Описания функций, для которых надо строить графики begin ... end; function f3 (...): real; begin ... end; для построения графика функции f1(x) вызов procedure график (x: real, function f(x: real)): real; процедуры имел бы вид: график(x, f1(x)); begin
writeln(.........., f(x), ...); end; Конкретный вид функции указывался бы при вызове. В каждом конкретном случае вызывалась бы та функция, которая передавалась бы в качестве фактического параметра. На Паскале в качестве формальных параметров допускается использовать имена процедур или функций. При вызове подпрограммы на место формальных параметров процедур или функций осуществляется подстановка имен соответствующих им фактических процедур или функций. Но на Паскале нельзя описывать функцию как параметр в той форме, как это сделано выше (нельзя определять сложный тип прямо в заголовке). С этим мы уже сталкивались при передаче массивов как параметров. Procedure f(a: array[1..10] of char);
f: function (x: real); …): real; На Паскале для этого случая нужно объявить процедурный тип.
func = function (x: real): real; Справа от " =" фактически приводится заголовок функции, только без имени. Процедурный тип определяет, какой вид подпрограммы (процедуру, функцию) можно использовать в качестве параметра и с какими параметрами должна вызываться эта подпрограмма. Фактически соответствующий формальный параметр будет являться параметром-значением, т. к. записывается без зарезервированного слова var. Правильный заголовок процедуры построения графика в этом случае должен иметь следующий вид: procedure plot (x: real; f: func); begin ... writeln (..., f(x), ...); end; Переменной процедурного типа f может быть присвоено в качестве значения имя функции, например f1 (соответствующего по типу и числу параметров вида). Выполнения функции f1 при таком присваивании не происходит, но при вызове вида f(x) вызываться будет f1. Примеры объявлений: Туре Proc_0 = Procedure; {тип-процедура без параметров} Ргос_2 = Procedure(Var х, у); {тип-процедура с 2-мя параметрами} Ргос_3 = Procedure(a, b: real; Var у: real); {тип-процедура с 3-мя параметрами } Func_0 = Function: integer; {тип-функция без параметров } Func_1 = Function(s: real): real; {тип-функция с одним параметром } Установлены следующие правила использования подпрограмм в качестве параметров: § правила совместимости: процедурные типы для совместимости по присваиванию должны иметь одинаковое число формальных параметров, а параметры на соответствующих позициях в заголовках, должны иметь эквивалентный тип. Кроме того, для функций должны совпадать типы возвращаемых значений; § согласно принципу опережающего описания объектов в Паскале, процедура или функция может обращаться только к той процедуре или функции, описание которых располагается перед описанием вызывающей процедуры или функции. § для установки правильных связей между вызывающими и вызываемыми подпрограммами вызываемые процедуры или функции (передаваемые как параметры) должны быть скомпилирована с использованием дальней модели вызовов с формированием их полного адреса, что достигается двумя путями: они должны компилироваться с директивой компилятору {SF+} 1-й путь {$F+} function f1(...): real;
... end; {$F-}
2-й путь: function f1(...): real; far; явно указана необходимость использования дальнего вызова в заголовке функции.
§ они не должны объявляться внутри других процедур или функций; § они не должны быть стандартными процедурами или функциями;
18.13 Директивы подпрограмм Следующие директивы дают дополнительную информацию транслятору о размещении подпрограмм: 1. Директива FORWARD Директивы FAR и NEAR Как правило, компилятор Turbo Pascal автоматически выбирает адресацию к подпрограмме. Например, если подпрограмма находится в одном файле с основной программой, то она компилируется с " ближним" (near) адресом входа и возврата, состоящим только из смещения в текущем сегменте, а если она находится в модуле, то формируется " дальний" (far) адрес, состоящий из адреса сегмента и смещения. Так, например, если подпрограмма используется для переменных процедурного типа, она независимо от своего расположения должна компилироваться с получением " дальнего" адреса. Реже используется директива near, которая сообщает компилятору, что подпрограмму следует компилировать с получением именно такого адреса. Эта директива эквивалентна ключу компилятора {$F-}, который выбирается по умолчанию. Действие директивы распространяется только на одну подпрограмму. Директива EXTERNAL Директива external позволяет использовать в программе подпрограммы, написанные на языке ассемблера и скомпилированные отдельно. Эти подпрограммы должны быть скомпонованы с основной программой, используя ключ {$L < имя файла> }. Здесь имя файла - имя того файла (с расширением.OBJ), в котором находятся скомпилированные объектные модули подпрограмм, написанных на языке ассемблера. Директива ASSEMBLER Директива assembler позволяет написать подпрограмму полностью на языке ассемблера. При этом во время компиляции подпрограмма будет автоматически скомпилирована встроенным ассемблером пакета Turbo Pascal. При отладке такой подпрограммы можно использовать встроенный отладчик пакета. Директива INLINE Директива позволяет включить в текст программы команды, записанные непосредственно в машинных кодах. В отличие от других подпрограмм подпрограмма с директивой inline непосредственно добавляется всюду, где есть ее вызов (фактически она является макроопределением). Такие подпрограммы могут иметь параметры, которые можно использовать в тексте подпрограммы, получая их из стека. Машинные коды в процедуре записываются в круглых скобках побайтно через прямой слэш (/). 6. Директива INTERRUPT Директива interrupt предназначена для процедур, обрабатывающих прерывания. Такие процедуры имеют стандартный заголовок: procedure IntHandler(Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP: Word); interrupt; begin … end; В заголовке отдельные параметры можно опускать (но только с начала списка), промежуточные параметры удалять нельзя, например: procedure IntHandler(DI, ES, BP: Word); interrupt; {неправильный заголовок} procedure IntHandler(DI, DS, ES, BP: Word); interrupt; {правильный заголовок} Нельзя в заголовке процедуры обработки прерываний записывать и какие-либо другие параметры. 18.14 Отладка и тестирование программ, содержащих подпрограммы Практикуются два вида тестирования: нисходящее тестирование и восходящее тестирование. При тестировании программы и ее подпрограмм обычно не обойтись без комбинации этих методов.
18.14.1 Нисходящее тестирование и подпрограммы-заглушки Независимо от того, как создается программная система (основная программа, в которой имеются подпрограммы), программистом-одиночкой или коллективом программистов, не все подпрограммы обычно бывают готовы одновременно. Тем не менее и при этих условиях возможно выполнить тестирование обмена управляющими сигналами между основной программой и ее подпрограммами первого уровня, а также тестирование и отладку уже готовых программ. Процесс тестирования обмена управляющими сигналами между основной программой и ее подпрограммами называется нисходящим тестированием. Поскольку для выполнения тестирования основная программа нуждается во всех подпрограммах первого уровня, отсутствующие пока подпрограммы необходимо заменить т.н. подпрограммами-заглушками (stub). Заглушка состоит; головка процедуры или функции, за которым следует тело, содержащее минимум операторов. Эти операторы должны выводить сообщение о выполнении по программы, а также присваивать простые значения любым выходным данным.
18.14.2 Восходящее тестирование и программы-тестеры Конечно, готовую подпрограмму, если в ней обнаружатся неполадки, всегда можно заменить заглушкой, тем не менее, предварительное тестирование новой подпрограммы следует выполнять обязательно. Искать и исправлять ошибки в отдельной подпрограмме всегда легче, чем в готовой программной системе. Вы сможете протестировать новую подпрограмму, воспользовавшись небольшой программой-тестером. Не тратьте много времени на создание элегантной программы-тестера — эта программа окажется не нужна в момент, когда тестирование новой подпрограммы будет завершено. Программа-тестер должна содержать только раздел описаний, да минимум операторов, необходимых для тестирования единственной подпрограммы. Данная программа должна начинаться со считывания или присваивания значений всем входным параметрам, а также параметрам входа-выхода, после чего следует вызов тестируемой подпрограммы. В завершение программа-тестер должна отображать результаты работы подпрограммы. После того как вы уверены, что подпрограмма работает должным образом, замените ее в программной системе заглушкой. Процесс раздельного тестирования самостоятельных подпрограмм, прежде чем собрать их в программную систему, называется восходящим тестированием. Комбинация восходящего и нисходящего тестирований даст вам уверенность, что программная система после сборки окажется сравнительно свободна от ошибок. В результате заключительная отладка будет выполнена быстро и без проблем. 18.14.3 Рекомендации по отладке программ, содержащих подпрограммы Вот несколько рекомендаций по отладке программных систем.
Очень полезно планировать отладку в процессе создания каждой подпрограммы, а не добавлять отладочные операторы впоследствии. При создании подпрограмм, добавляйте в них отладочные операторы, о которых шла речь в рекомендациях 2 и 4 выше. Когда убедитесь, что подпрограмма работает как нужно, эти операторы несложно будет " изъять из обращения". Для этого проще всего превратить такие операторы в комментарии, заключив их в фигурные скобки, Поэтому же, если возникнут какие-либо проблемы, эти комментарии не составит труда снова сделать исполняемыми операторами, удалив фигурные скобки. Другой способ включения-выключения отладочных операторов состоит в чтобы использовать глобальную константу типа Boolean (например, Debug), объявленную в основной программе. При этом, когда потребуется отладка, в основной программе должно быть следующее описание данной константы. const Debug = True {отладка включена} А когда надобность в отладке минует, данное описание константы дол выглядеть так: const Debug = False {отладка выключена} Затем в тело основной программы, а также в подпрограммы вставьте диагностические операторы WriteLn, управляемые оператором if с Debug в качестве условия.
18.14.4 Использование отладчика для трассировки процедур В качестве альтернативы отображению начальных значений входных (и входа-выхода) параметров процедуры с использованием специальных отладочным операторов, для получения соответствующей информации можно воспользоваться отладчиком Turbo Pascal и его возможностью трассировки Trace into (< F7> ). Если оператор, который должен быть выполнен следующим, представляет собой оператор вызова процедуры, нажатие клавиши < F7> обеспечит вход в эту процедуру. В результате в окне редактора появится тело этой процедуры с указателем выполнения на слове begin. Если вы укажете параметры процедуры как переменные для наблюдения, исходные значения этих параметров появятся в окне Watch. Затем, с выполнением каждого оператора процедуры (для этого каждый раз требуется. нажимать клавишу < F7> ), любое новое значение, присвоенное выходному параметру или параметру входа-выхода, будет отображено в окне Watch. После того как процедура возвратит управление основной программе, вы можете воспользоваться отладчиком чтобы выяснить, какие значения были возвращены в программу. На этот раз в качестве переменных для наблюдения следует указать все фактические параметры, которые соответствуют выходным параметрам или параметрам входа-выхода. В результате значения, возвращенные процедурой, окажутся отображены в окне Watch. Популярное:
|
Последнее изменение этой страницы: 2016-07-12; Просмотров: 847; Нарушение авторского права страницы