Оформление и вызов программных единиц в системе QBasic
Во входном языке системы QBasic нет специальных средств для выделения головной программы. Поэтому все, что не принадлежит специальным образом оформленным подпрограммам или функциям, относится к тексту головной программы. Дополнительной особенностью Бейсика является возможность использования как внешних, так и внутренних программных единиц. Тексты последних непосредственно входят в состав головной программы или тела любой другой программной единицы.
Описанию внутренней функции с именем, обязательно начинающимся с символов "FN", предшествует заголовок вида:
DEF FNs1s2...[(аргументы)]
Вслед за ним располагаются обычные операторы, участвующие в вычислении значения функции, завершающиеся обязательным присвоением вычисленного результата:
FNs1s2. . . = выражение
Признаком конца описания внутренней функции является оператор END DEF. В теле внутренней функции может находиться оператор EXIT DBF, обеспечивающий досрочное завершение вычислений при выполнении определенных условий.
Внутренняя функция, алгоритм вычисления которой сводится к единственному выражению, описывается одним оператором, после которого завершение END DEF не требуется:
DEF FNs1s2. . . [(аргументы)]=выражение
В операторах вычисления значения внутренней функции наряду с ее аргументами могут использоваться любые переменные той программной единицы, которой принадлежит внутренняя функция. Прибегать к ним, как правило, не рекомендуется, т. к. нет никакой гарантии, что два значения функции при одних и тех же аргументах будут совпадать. Между последовательными обращениями к функции значения использованных переменных головной программы могли измениться. Однако и исключать их использование вообще тоже нельзя. Примером полезного применения внутренней функции без аргументов может служить вычисление расстояния между двумя точками, которое может потребоваться в разных местах программы после модификации координат одной или обеих точек:
DEF FNd=SQR((X1-X2)^2+(Y1-Y2)^2+(Z1-Z2)^2)
Эффект от использования такой функции заключается в сокращении длины текста программы — 37 символов приведенного выражения в точках вызова функции заменяются всего на 3 символа.
Все программные единицы в Си носят название функций и разницу в оформлении настоящих подпрограмм и настоящих функций можно обнаружить по типу возвращаемого значения. Если в качестве указания типа использовано служебное слово void, то перед нами типичная подпрограмма (в терминах Си — функция, не возвращающая значение). Перед заголовком объявляемой функции всегда присутствует либо стандартный тип, либо описатель типа с последующей звездочкой. Последнее означает, что функция возвращает указатель на данное соответствующего типа. В частности, допускается, что таковым указателем может быть "void *" В самом общем виде заголовок функции выглядит следующим образом:
void имя_функции([параметры])
ИЛИ
тип имя_функции([параметры])
ИЛИ
тип * имя_функции([параметры])
Тело функции, расположенное после заголовка, заключается в фигурные скобки. Если функция возвращает значение, то в ее теле должен присутствовать хотя бы один оператор return с указанием возвращаемого значения. Например:
int sign(int x)
{
/* Определение знака целого числа */
if(x<0) return -1;
if(x>0) return 1;
return 0; }
В отличие от Бейсика и Паскаля функции Си, не имеющие параметров, всегда сопровождаются пустыми круглыми скобками. Например — cirscr ().
Головная программа на Си представлена функцией main, которая может иметь до трех параметров, связанных с извлечением аргументов из командной строки. Чаще всего эта функция не имеет параметров и не возвращает значение:
void main(void)
К функции-подпрограмме в Си обращаются, указав ее имя со списком фактических параметров:
имя_функции(фактические параметры);
Функция, возвращающая значение, может использоваться как операнд в выражении соответствующего типа:
int qq;
qq=getch(); /*ожидание ввода кода символа с клавиатуры*/
Однако в большинстве примеров предыдущих глав вы могли заметить, что к функции getch обращаются и как к обычной подпрограмме, игнорируя возвращаемое значение. Это правило распространяется на любые функции Си. Конечно, не каждая из них в этом варианте может оказаться полезной. Например, допустимо, но нелепо встретить строку:
sin(0.5);
Системная функция в данном случае проработает, но эффекта от такого вызова никто не заметит. Разве что немного увеличится время работы программы. А в случае с функцией getch игнорирование результата позволяет зафиксировать момент, когда пользователь нажал какую-то клавишу.
Операторы вычисления значения внутренней функции срабатывают только при вызове функции из головной программы. Если Бейсик-интерпретатор попадает на них другим способом, то тело внутренней функции обходится.
Внешняя функция начинается с заголовка:
FUNCTION имя_функции[(аргументы)][STATIC]
Добавка STATIC требует, чтобы значения всех локальных переменных после очередного обращения к функции были сохранены и моглиут быть использованы при последующем обращении к функции. Если по условиям задачи необходимо сохранять значения только некоторых локальных переменных, то слово STATIC может быть использовано в теле функции при объявлении соответствующих переменных:
STATIC Q1 AS INTEGER, F AS DOUBLE
В теле функции, состоящем из обычных операторов, обязательно должно присутствовать присвоение:
имя_функции = выражение
Если имя функции используется в правой части оператора присвоения или в условном выражении, то вслед за ним в круглых скобках должны быть указаны аргументы и это означает рекурсивный вызов самой себя.
Признаком конца описания внешней функции является оператор END FUNCTION. Для досрочного выхода из подпрограммы вычисления функции используется оператор EXIT FUNCTION.
Тип внутренней или внешней функции определяется специальным символом, завершающим имя функции (% — короткий целый, & — длинный целый, ! — короткий вещественный, # — длинный вещественный, $ -строковый). Если имя функции завершается любым другим символом, то по умолчанию функции присваивается короткий вещественный тип.
Для вычисления значения внутренней или внешней функции достаточно использовать ее имя с набором фактических аргументов в качестве операнда выражения соответствующего типа.
Внутренняя подпрограмма, входящая в текст головной программы, обязательно должна начинаться с метки, на которую передается управление оператором GOSUB. Работа внутренней подпрограммы завершается оператором RETURN, после чего продолжается работа основной программы с оператора, следующего за GOSUB. В отличие от обычных подпрограмм у внутренней подпрограммы отсутствуют параметры, и она может оперировать только с переменными основной программы. Логически внутренние подпрограммы повторяют структуру подпрограмм в машинных кодах. К внутренней подпрограмме может обратиться только ее владелец.
Внешняя подпрограмма начинается с заголовка:
SUB имя-подпрограммы[(параметры)][STATIC]
Тело внешней подпрофаммы завершается оператором END SUB. Для досрочного прекращения работы программы используется оператор EXIT SUB.
К внешней подпрофамме можно обратиться из любой профаммной единицы двумя способами, особой разницы между которыми нет:
имя_подпрограммы [фактические параметры]
ИЛИ
CALL имя_подпрограммы[(фактические параметры)]
Как дань своему прототипу, — алгоритмическому языку АЛГОЛ, — Паскаль называет свои подпрограммы процедурами и начинает их описание со строки вида:
procedure имя_процедуры[(параметры)];
Тело процедуры на Паскале в точности повторяет структуру головной программы. В нем могут присутствовать разделы описания меток (label), констант (const), типов данных (type), переменных (var) и других процедур и функций, входящих в состав данной процедуры. Наличие вложенных процедур и функций отличает Паскаль и от Си, и от Бейсика. Собственно вычислительная часть тела процедуры начинается со служебного слова begin и заканчивается соответствующим end, после которого, в отличие от головной программы, следует не точка, а точка с запятой.
Фрагмент программы от заголовка процедуры до завершающей операторной скобки end принято называть блоком. Для Паскаля характерна возможность использования вложенных блоков, с каждым из которых можно связать натуральное число, соответствующее уровню вложения. Так, например, в блок А могут быть вложены два последовательных блока первого уровня — блоки в и с. Если же блок с вложен в блок в, входящий в свою очередь в блок А, то блок с уже имеет уровень два по отношению к блоку А. В теле любого блока могут содержаться обращения к вложенным блокам своего первого уровня или к последовательным блокам такого же уровня. Блок не имеет права напрямую обратиться к своим процедурам второго или более высокого уровня вложенности.
Если процедура А обращается к процедуре в, то описание процедуры в должно предшествовать описанию процедуры А. Однако, если программа включает рекурсивную цепочку из двух или более процедур, одна из них должна объявляться (но не описываться) первой с добавкой forward. Соответствующее объявление носит название опережающего (forward — впереди бегущий футболист, нападающий):
procedure имя_процедуры[(параметры)]; forward;
Описание опережающей процедуры располагается после других процедур рекурсивной цепочки, но в ее заголовке список параметров может уже не указываться.
Для обращения к процедуре достаточно задать ее имя с последующим набором фактических параметров.
Описание функции отличается только тем, что ее заголовок начинается со служебного слова function и заканчивается указанием типа функции.
function имя_функции[(параметры)]:тип;
Тело функции отличается от тела подпрограммы только наличием оператора присваивания:
имя_функции:=выражение;
Досрочный выход из тела функции или тела процедуры осуществляется по оператору exit.
Головная программа на Паскале выступает в роли контейнера, содержащего все свои функции и процедуры. Первым оператором головной программы может быть не обязательная строка:
program имя_программы;
Собственно тело головной программы начинается с операторной скобки begin, расположенной вслед за последним объявлением, и завершается закрывающей операторной скобкой end с последующей точкой.