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

При создании программ часто появляется некоторая последовательность команд (инструкций), которую необходимо выполнять в нескольких местах.. Можно эту последовательность переписать несколько раз в нужных местах, но, во первых, это удлинит текст программы и уменьшит ее читабельность (последнее, несомненно, хуже для программиста), но и существенно увеличит вероятность внесения ошибок в программу. Можно попытаться обойти эту проблему с помощью GO TO. Но это потребует дополнительной логики, реализованной с помощью флагов, что опять повышает вероятность ошибок.

В большинстве языков программирования для решения этой проблемы введено понятие подпрограммы (название зависит от языка). Подпрограмма – это «кусок» кода, который вынесен из основной программы и которому дано уникальное имя. Тогда в месте, где необходимо выполнить эту последовательность кода, необходимо просто сослаться на  имя подпрограммы. Это называется вызовом подпрограммы (процедуры, функции,…).

Например, мы хотим несколько раз напечатать несколько одинаковых строчек.

print *,’---------------------------‘

print *,’*********************‘

print *,’---------------------------‘

Вместо того, чтобы несколько раз в теле программы набирать (или копировать, что несколько лучше:) эти 3 строки, можно оформить их в виде подпрограммы:

subroutine PrtSomething()               ! имя подпрограммы

print *,’---------------------------‘

print *,’*********************‘

print *,’---------------------------‘

end

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

call PrtSomething()

 

Теперь усложним задачу. Нужно выполнить печать значения разных переменных опять-таки в нескольких местах программы.

Важно запомнить одно правило: подпрограмма НИЧЕГО не знает о том, кто и когда ее вызывает! Для нее не существует ничего вне ее кода. Она НИЧЕГО не знает о переменных вне ее. Можно представить ее живущей в тюремной камере без окон. Все, что ей нужно, передается через окошечки в стене, над которыми написано названия формальных параметров. Подпрограммы общается с внешним миров через ИНТЕРФЕЙС. Это правила общения подпрограммы с внешним миров. Для того, чтобы передать какое-то значение в подпрограмму, его кладут в окошечко с названием какого-то формального параметра. Подпрограмма берет это значение по имени формального параметра (надпись над окошком).

Другими словами, чтобы сделать доступными какие-то данные подпрограмме, их надо передать в списке параметров, который стоит сразу за именем подпрограммы в скобках: subroutine PrеValues(a,b,c). Параметры, записанные в заголовке подпрограммы, называются формальными параметрами (аргументами). Для подпрограммы это просто  переменные. Параметры, подставленные в месте вызова подпрограммы, называются фактическими параметрами. В качестве фактических параметров могут быть константы, переменные или (неявные) результаты выражений.

 

subroutine PrеValues(a,b,c)             ! имя подпрограммы

!    описание переменных – формальных параметров

            integer a,b

            real c

!

print *,’a= ‘,a

print *,’b= ‘,b

print *,’c= ‘,c

return

end

В подпрограмму PrеValues(a,b,c)   передаются значения 3-х переменных, которые и печатаются. Формальные параметры – переменные, существующие ТОЛЬКО в подпрограмме. Они ВИДНЫ ТОЛЬКО в подпрограмме. Если вне подпрограммы есть переменные с такими же именами, они не имеют ничего общего!

Вызов этой подпрограммы может выглядеть так:

integer a1/4/,a2/7/

real pi/3.14159/

call PrеValues(a1,a2,pi)        !напечатает             4          7          3.14159

call PrеValues(1,2,7.)            ! 1        2          7.

call PrеValues(1,2.,7.)           ! ERROR!

При первом вызове подпрограммы в переменную (форм. пар-ер) а будет положено значение из переменной  a1(4), в b – из a2 (7), в c – из pi (3.14…). Соответственно подпрограмма и распечатает их в таком виде:

a=4

b=7

            c=3.14159

Параметры, с которыми вызывается подпрограмма, называются фактическими. a1,a2,pi  фактические параметры при первом вызове, 1,2,7. фактические параметры при втором.

Почему неправильный последний пример? Интерфейс подпрограммы предписывает, что подпрограмма ДОЛЖНА вызываться с 3-мя фактическими параметрами, имеющими соответствующий тип. Соответственно она и распределяет память. Если вызвать с фактическими параметрами, имеющими другой тип, значение положится в вызывающей программе в память по одному, а внутри подпрограммы будет взято из той-же памяти по другому. Результат становится полностью неопределен! Отсюда вытекает следующее правило:

Список формальных и фактических параметров должен совпадать по количеству и характеристикам аргументов.

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

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

subroutine Sum(a,b,c)

            real a,b,c

            c=a+b

return

            end

call Sum(1.,3.,s)         

В переменную s  поместится результат сложения фактических параметров – констант 1 и 3. Важно обратить внимание, что если формальный параметр в подпрограмме является [in] – входящим (вводящим значение в подпрограмму), то в качестве фактического параметра может быть переменная или константа (первые 2 параметра Sum). Если же формальный параметр в подпрограмме является [out], или [in/out] – выходящим (возвращающем значение из подпрограммы), то в качестве фактического параметра может быть только переменная (3 параметр Sum).

Если подпрограмма возвращает только одно значение, как в последнем случае, лучше использовать другой тип подпрограмм – подпрограмму-функцию. Формально он описывается так:

FUNCTION < имя функции>( [< формрг >])

< объявление формрг >

< объявление локальных объектов >

...

< вычисляемые операторы, присваивание результата >

END [ FUNCTION [ < имя функции > ] ]

 

Пример:

function Sum_(a,b)

real a,b,Sum_

            Sum_=a+b

            return

end

s=Sum_(1.,3.)

Результат полностью аналогичен пред.

На что надо обратить внимание: имя функции как бы является формальным параметром, имеет тип и ему должно быть присвоено какое-то значение. Имя функции выбрано Sum_, а не Sum, т.к. последнее часто является зарезервированным в разных языках.

            Теперь рассмотрим задачу – составить подпрограмму нахождения мин в одномерном массиве. Т.к. поиск мин/макс – частая задача, совершенно логично заключить ее в виде подпрограммы-функции. Теперь об интерфейсе. Подпрограмма должна принимать в качестве входных данных массив. Далее в цикле она будет искать мин. Но подпрограмма не знает размер массива! Следовательно, вторым параметром надо передать подпрограмме кол-во элементов в массиве, которые надо обработать.

            program sub_min

 

            implicit none

            real a(10),min,GetMin

            integer i,n

 

a=(/(50/i,i=1,5),(i*10,i=1,5)/)           ! 50.00000       25.00000       16.00000       12.00000       ! 10.00000       10.00000       20.00000       30.00000       ! 40.00000       50.00000

           

            n=10

            min=GetMin(a,n)

 

            print *,a

            print *,min

 

            end program sub_min

 

            real function GetMin(ar,n)

                        real ar(*)

                        integer n

                        GetMin=ar(n)

                        do n=n-1,1,-1

                                   if (ar(n)<GetMin) GetMin=ar(n)

                        end do

                        return

            end

 

 

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

В подпрограмме используется один спорный прием. Для уменьшения кол-ва переменных в качестве переменной цикла используется формальный параметр, через который был передан размер массива. Но если при вызове в качестве фактич. параметра использовалась константа (min=GetMin(a,10)), то программа вызовет AV ошибку обращения к памяти, т.к. внутри подпрограммы попытается положить по адресу константы новое значение. Для надежности лучше такую «оптимизацию»  не допускать. Более того, даже если при вызове подпрограммы фактическим параметров являлась переменная, хранящее кол-во элементов массива, то после вызова GetMin ее значение будет равно 0! Пользователь вашей подпрограммы может ничего не знать об этом.

            Как это работает: при вызове первым параметром является массив a, вторым – размер массива. Затем происходит переход на выполнение подпрограммы. Значения формальных переменных – ar – является массивом а, n =10.

После выполнения оператора return переход обратно в вызывающую программу. Там вместо подпрограммы уже подставляется число – результат, который и помещается в переменную s.

min=GetMin(a(1:4),n)

Еще одно замечание: внутри подпрограммы можно объявлять любые переменные. Но, как уже говорилось, подпрограмма отделена от остального мира. Поэтому эти переменные существуют только внутри подпрограммы и не видны остальному миру! Кроме того, после выхода из подпрограммы (по оператору return или end) и повторного ее вызова значения локальных переменных внутри подпрограммы не сохраняются. (Этого можно достичь с помощью оператора/атрибута SAVE).

 

Когда использовать подпрограммы? Есть мнение, что если в какой-то программе больше 10 строк кода, то ее надо разбить на подпрограммы. Общие правила написания структурированных, хорошо читаемых программ – если некоторый участок кода имеет какое-то обособленное смысловое значение – выделяйте его в виде подпрограммы. Это дает несколько преимущества: удобство чтения, уменьшение вероятности внесения ошибок, уменьшения кол-ва переменных, удобство отладки.

 

 

Вызов подпрограмм может быть вложенным (рис. 5). Вообще говоря, выполнение начинается с главной программы. Во многих языках она называется MAIN. В Fortran-е – она не имеет названия и не имеет заголовка в отличие от всех остальных подпрограмм.

 

Более того, подпрограмма subroutine2 может вызвать опять subroutine1:

            ………………………………

            n=1

            call subroutine1(n)

            ………………………………

 

            subroutine subroutine1(n)

                        integer n

                        if (n<5) then

                                   n=n+1

                                   call subroutine2(n)

                        end if

                        return

            end

            subroutine subroutine2(n)

                        integer n

                        if (n<5) then

                                   n=n+1

                                   call subroutine1(n)

                        end if

                        return

            end

 

рис. 5

 

 

 

 

Сведения из стандарта для справки.

Структура программы

Фортран программа состоит из одной или нескольких программных блоков (units).

Программный блок (ПБ) – это, обычно, последовательность операторов, определяющих данные и действия, которые необходимо предпринять для выполнения вычислений. Завершается ПБ оператором END.

ПБ может быть:

Главной программой (main program);

Внешней подпрограммой (external subprograms);

Модулем (module);

Блоком Данных (block data).

Исполняемая программа содержит 1 главную программу и, возможно, любое количество любых ПБ. ПБ могут компилироваться отдельно.

Внешняя подпрограмма – это  function или subroutine, которая не содержится внутри главной программы, модуля, или другой подпрограммы. Она определяет алгоритм, который должен быть выполнен, и может быть вызвана из других ПБ. Модули и Блоки Данных не исполняемы. (Modules can contain module procedures, though, which are executable.)

Модули содержат определения, которые могут быть сделаны доступными другим ПБ: определения типов и данных, определения процедур (module subprograms) и интерфейсы процедур (procedure interfaces)

Module subprograms могут быть или functions или subroutines. Они могут быть вызваны другими module subprograms в модуле, или другими ПБ, которые имеют доступ к модулю.

Блок данных (block data) определяет начальные значения объектов данных в именованном common blocks. В Fortran 95/90  блок данных может быть заменен модулем.

Главная программа, внешние подпрограммы, и подпрограммы модулей (module subprograms) могут содержать внутренние подпрограммы (internal subprograms). Объект, содержащий внутренние подпрограммы называется хозяином (host). Внутренние подпрограммы могут быть вызваны только своим хозяином или другими внутренними подпрограммами в том же хозяине. Внутренние подпрограммы не могут содержать внутри себя другие внутренние подпрограммы (т.е. не иерархичны).

 

 

 

Последовательность выполнения

Если программа содержит фортрановскую главную программу, выполнение начинается с первой исполняемой конструкции главной программы. Выполнение любой программы заключается в выполнении исполняемых конструкций в области программы. Когда программы вызвана  выполнение начинается с первой исполняемой конструкции после вызванной точки входа. За исключением приведенных ниже условий эффект выполнения как если бы исполняемые конструкции выполняются в том порядке, в котором они записаны в программе до тех пор, пока не будет выполнен один из операторов STOP, RETURN, or END. Исключения:

1 – Выполнение оператора ветвления изменяет последовательность выполнения. Эти операторы явно  определяют новую точку исполнения;

2 -  CASE, DO, IF, или SELECT TYPE конструкция содержит структуру внутренних операторов, и выполнение этих конструкций приводит к неявному внутреннему ветвлению;

3 - END=, ERR=, and EOR= спецификаторы могут приводить к ветвлению;

4 Альтернативный Return может приводить к ветвлению.

Внутренняя подпрограмма может находиться до оператора END. Последовательность выполнения исключает все подобные определения.

Нормальное прекращение выполнения программы происходит если будет выполнен или оператор STOP или оператор конца программы.

 

Встроенные функции

 

 

НА ГЛАВНУЮ ДАЛЕЕ
Hosted by uCoz