Пусть мы хотим составить программу, для работы которой требуется неоднократное выполнение операции извлечения квадратного корня. Квадратный корень извлекается достаточно просто, и мы могли бы, вообще говоря, включить в программу соответствующую последовательность команд везде, где должны вычисляться значения корней. Однако это, во-первых, неудобно, а во-вторых, может служить дополнительным источником ошибок.
Предположим теперь, что мы написали одну программу вычисления квадратного корня и обращаемся к ней всякий раз, когда нужно извлечь корень. Программа вычисления квадратного корня выступает в данном случае в качестве подпрограммы. Наша основная программа по отношению к ней является вызывающей программой. Каждый раз при необходимости извлечь квадратный корень производится вызов подпрограммы. Итак, применение подпрограммы позволило нам писать необходимую последовательность команд всего один раз. Для извлечения квадратного корня производится вызов подпрограммы, которая и выполняет требуемые действия.
Обычно вызов подпрограмм осуществляется с помощью команды BAL, описываемой в следующем разделе. Другими словами, при вызове мы просто передаем управление подпрограмме. По окончании работы подпрограммы должен быть предусмотрен возврат управления основной (вызывающей) программе, причем обычно непосредственно в то место, откуда был совершен переход. Рисунок 13.1 иллюстрирует принцип использования подпрограмм.
При вызове подпрограммы ей передается управление. Однако ей также следует указать информацию, которую надлежит обрабатывать. Например, при вызове подпрограммы извлечения корня ей необходимо задать число, из которого корень следует извлекать. Подпрограмма, возвращая управление вызывающей программе, передает ей также полученный результат. Числа, передаваемые подпрограмме, а также возвращаемые ею основной программе, называются параметрами подпрограммы. Каждый параметр, вообще говоря, представляет собой единицу информации, передаваемую подпрограмме при вызове или возвращаемую ею при возврате. Параметры могут быть не только числовыми. Например, при использовании подпрограммы, располагающей некоторое множество чисел в порядке возрастания, параметрами могут служить общее количество чисел и адрес первого из них.
Теперь мы рассмотрим более сложную ситуацию. В принципе любая подпрограмма может сама являться вызывающей. Попытаемся, например, разобраться в том, что происходит при использовании макрокоманды RWD. Командами, входящими в RWD, производится вызов подпрограммы $$IO, которая в свою очередь обращается к подпрограммам ввода-вывода операционной системы, запрашивающим образ очередной перфокарты. Этот запрос данных тоже представляет собой вызов подпрограммы. Итак, $$IO производит вызов системной подпрограммы, которая получает необходимые данные, выполняя еще один вызов. По окончании работы системной программы адрес образа только что считанной карты передается программе $$IO, которая переводит его в двоичную форму и после этого возвращает основной программе. Описанная последовательность вызовов схематически изображена на рис. 13.2. Рассмотренный процесс позволяет значительно облегчить программирование. Вам, т. е. пользователям, нет нужды заботиться об организации взаимодействия с операционной системой и различного рода преобразованиях данных. Вместо этого достаточно просто вызвать определенную подпрограмму. Аналогичным образом авторам программы $$IO совсем не нужно было писать программы ввода-вывода в явном виде; они воспользовались макрокомандой GET, действие которой описывается в гл. 17, для вызова подпрограмм, составленных системными программистами фирмы IBM. Короче говоря, пользователям предоставлена возможность уделять больше внимания их собственным задачам, предоставляя заботу о многих деталях подпрограммам системы.
Еще одно преимущество работы с использованием подпрограмм заключается в значительном упрощении модификации крупных пакетов программ и снижении опасности привнесения ошибок при такой модификации. Возвращаясь к задаче извлечения корня, предположим, что мы нашли какой-либо более эффективный способ получения результата или что нам теперь понадобилось вычислять не квадратный корень, а кубический. Если бы процесс извлечения не был организован с помощью подпрограммы, то нам пришлось бы просмотреть всю основную программу целиком, внося изменения там, где производились соответствующие вычисления. Превосходный способ внесения ошибок! Если бы мы, напротив, использовали подпрограмму извлечения квадратного корня, то внести исправления нужно бы было только в нее. Ошибки, если бы они при этом и появились, были бы, по крайней мере, сконцентрированы в одном месте.
Рис. 13.1. Вызывающая программа и подпрограмма.
Рис. 13.2. Вложенные вызовы подпрограмм при использовании макро RWD.