1.Функции и подпрограммы: описание, реализация.
На функцию ссылаются в выражении, и она возвращает величину, которая используется при вычислении этого выражения. Существует три вида функций:
Встроенные функции
Внешние функции
Функции-операторы
Ссылка на функцию может появиться в арифметическом или логическом выражении. Когда выполняется ссылка на функцию, функция вызывается, а величина результата используется как операнд в выражении, которое содержит ссылку на функцию. Форма ссылки на функцию следующая:
переменная = имя-функции ([пар[,пар]...])
Имя-функции - это определенное пользователем имя внешней или встроенной функции или функции-оператора.
пар - это фактический параметр.
После основного текста программы расположен оператор contains (содержит, вмещает), означающий, что далее будет описана функция, используемая в текущей программе.
Функция, объявленная внутри программы после оператора contains, называется внутренней функцией.
Для вызова внутренней функции используется следующая схема:
тип function имя_функции (формальные параметры)
типы формальных параметров
операторы описания
исполняемые операторы
имя_функции=вычисленное значение
end function имя_функции
Пример:
program understanding_function
real, parameter :: pi=3.1415926
real x, dx, s
x=pi/8; dx=0.01
s=f(x+dx)/f(x-dx)+f(x)
write(*,"(A,f5.2)") "Result..." ,s
contains
real function f(xt)
real xt
f=(sin(xt)+cos(xt))/(xt+exp(xt))
end function f end
Аргументы, используемые при описании функции, носят название формальных параметров. Аргументы, которые используются при вызове функции, называются фактическими параметрами.
Пример (функция без аргументов):
program funct
write(*,*) Pi(), Pi()/2
contains
real function Pi()
Pi=3.1415926
end function Pi
end
Подпрограммы.
Кроме функций, существует второй вид процедур - подпрограммы.
Если функция возвращает вычисленное значение, то подпрограммы позволяют выполнять ряд определенных действий, например осуществлять специальные операции ввода-вывода, обрабатывать массивы и др.
Подпрограмма записывается отдельно от основной программы, после оператора contains.
Чтобы создать подпрограмму, применяется следующая схема:
subroutine имя_подпрограммы (формальные параметры)
типы формальных параметров
операторы описания
исполняемые операторы
end subroutine имя_подпрограммы
Вызов подпрограммы происходит при помощи оператора call.
call имя_подпрограммы(фактические параметры)
Пример. Подпрограмма, выводящая сообщение об отношении двух чисел.
program ratio
integer а, b
call show(90,23)
call show(50,50)
contains
subroutine show(a, b)
integer а,b
if (a<b) then
write(*,100) a," is smaller than ", b
elseif (a>b) then
write(*,100) a," is bigger than ", b else
write(*,100) a, " is equal ", b
end if
100 format (i4,A,i4)
end subroutine show
end
2. Внутренние, статические и автоматические переменные.
Между операторами function и end function, subroutine и end subroutine находятся операторы, выполняющие определенные действия. Очень часто эти операторы содержат дополнительные, вспомогательные переменные.
Чтобы использовать такие переменные в процедурах, их следует объявить
сразу после описания формальных параметров:
real function T(x) real x ! формальный параметр
integer k ! дополнительные перем.
real :: sum=0
do k=1,100
sum=sum+cos(k*x)/k
end do
T=sum
end function T end
Название "внутренние переменные" не случайно. Такие переменные доступны только в "своей", "родной" функции и недоступны в основном тексте программы и других подпрограммах и функциях.
При этом переменные, описанные после оператора program, доступны как в основной программе, так и в процедурах, описанных после оператора contains.
Если имена переменных основной программы совпадают с именами переменных во внутренних процедурах, то эти переменные будут совершенно разными!
B следующем примере переменные res, описанные в основной программе и подпрограмме, – разные переменные:
program prog
logical :: res=.TRUE. !переменная, доступная в основном тексте
call show()
write(*,*) "program res = ", res
contains
subroutine show()
logical :: res=.FALSE. !внутренняя переменная, доступна в show
write(*,*) "subroutine res = ", res
end subroutine show
end
По умолчанию все переменные, описанные в процедурах, являются статическими.
Это значит, что внутренние переменные, описанные внутри процедуры, создаются на этапе компиляции и существуют на всем протяжении работы программы.
program stat
write(*,100) "F1=", F1(), "F2=", F2()
write(*,100) "F1=", F1(), "F2=", F2()
write(*,100) "F1=", F1(), "F2=", F2()
100 format (2(А,i2))
contains
integer function F1() integer :: i=0
i = i + 5
F1=i
end function F1
integer function F2() integer i
i=0
i=i+5
F2=i
end function F2
end
B функциях F1() и F2() переменная i является статической.
Однако в функции F1() эта переменная инициализирована, т. e. на этапе компиляции ей присваивается начальное нулевое значение.
Поэтому переменная i содержит нуль только при первом обращении к функции, в последующих вызовах это значение увеличивается на 5.
Bo второй функции, F2(), переменной i присваивается нулевое значение при каждом обращении к функции и увеличение на 5 не происходит.
Тот факт, что переменные являются статическими, очень удобно использовать, например, для подсчета того, сколько раз вызывалась подпрограмма или функция.
Следует заметить, что по умолчанию все статические переменные имеют атрибут save (сохранить). Наряду с атрибутом save существует также атрибут static (статический), который эквивалентен атрибуту save и указывает, что переменные сохраняют свои значения после работы процедуры.
B следующем примере переменным a и b явно присвоены атрибуты save и static:
real, save :: а
logical, static :: b
B противоположность статическим переменным существуют автоматические переменные. Для таких переменных память отводится не на этапе компиляции, а при каждом обращении к процедуре при выполнении программы.
При вызове процедуры автоматические переменные создаются и размещаются в стеке (временной памяти) и по завершении работы процедуры из стека удаляются.
Чтобы объявить переменные автоматическими, используется атрибут или оператор automatic:
integer, automatic :: a,b,c
automatic integer a,b,c
B последнем случае атрибут automatic будет присваиваться неявно остальным переменным, описанным в процедуре.
Автоматические переменные не допускают инициализации, так как создаются заново при каждом вызове процедуры!
Резюме:
Статическим переменным, имеющим атрибуты save или static, память отводится на этапе компиляции. Переменные, которые инициализированы, всегда статические.
Автоматическим переменным, имеющим атрибут automatic, память отводится автоматически программой (без участия программиста) в специальной области памяти - стеке и освобождается после окончания работы процедуры.
Наконец, динамическим переменным, с атрибутом allocatable, память отводится во время работы программы или процедуры и полностью может контролироваться программистом при помощи операторов allocate и deallocate.
3. Управление работой подпрограмм.
Чтобы функция могла обработать исключительную ситуацию, следует воспользоваться оператором return (возврат), который прекращает выполнение функции и возвращает управление вызывающей программе или процедуре
program see_error
write(*,*) "F(1.0) = ", F(1.0)
write(*,*) ''F(2.0) = ", F(2.0)
contains
real function F(xt) real xt
if (abs(xt)<1e-30) then
F = 0.0
return
end if
F = 1.0/(xt-1.0)
end function F
end
Для создания более универсальных процедур можно оформить некоторые формальные параметры дополнительными, что дает определенную гибкость при создании процедур. Например, в следующем примере, если требуется, то можно выводить последовательность целых чисел с определенным шагом step.
program option_param
call line(1,10, 2)
contains
subroutine line(x1, xn, step) integer x,x1,xn,dx
integer, optional :: step
if (present(step)) then dx=step else dx=1
end if
do x=x1,xn,dx
write(*,"(i4,4)") x
end do
end subroutine line
end
B подпрограмме формальный параметр step является необязательным и поэтому объявлен с атрибутом optional (дополнительный).
Чтобы определить, используется ли при вызове процедуры необязательный параметр или нет, вызывается функция present (присутствие).
B конструкции if выполняется проверка: если вызов подпрограммы происходит с необязательным параметром, то функция present возвращает значение .TRUE. и dx= step, иначе результат .FALSE. и dx=1.
Таким образом, если необходимо вывести последовательность натуральных чисел, то можно вызвать подпрограмму с двумя обязательными параметрами, указывающими первое и последнее число последовательности, call line(1,10)
Подпрограмму можно вызвать также с дополнительным необязательным параметром step, указывающим на шаг последовательности, call line(1,10,2)
4.Внешние процедуры
Внешними называются процедуры, описанные отдельно от основного текста программы:
program prog
integer res
call ReadResult(res) call PrintResult(res)
contains
subroutine ReadResult(а) integer, intent(out) :: а
write(*,"(A,X)") "Enter value = "; read(*,*) a
end subroutine ReadResult
end
subroutine PrintResult(a) integer, intent (in) :: a
write(*,*) "Result = ",a**2
end subroutine PrintResult
Внешние процедуры могут находиться в одном файле с основной программой, в других файлах, библиотеках (lib-файлы), динамически подключаемых библиотеках (dll-файлы).
Внутренние процедуры не могут иметь внутренних процедур, внешние процедуры могут иметь внутренние процедуры.
Переменные, описанные во внешних процедурах, недоступны в основной программе. Переменные, описанные в основной программе, недоступны во внешних процедурах.
Внутреннюю процедуру нельзя передавать как параметр в другую процедуру, внешнюю можно.
Внутренние процедуры имеют явно заданный интерфейс, для внешних следует указывать явно, используя оператор interface (интерфейс):
если описана функция norm, вычисляющая длину вектора, то для того чтобы компилятор смог разобраться, что тип возвращаемого значения вещественное число (а не целое, на что указывает имя norm), в главной программе после оператора program используется оператор interface следующего содержания:
interface тип имя_функции(формальные параметры)
тип формальных параметров
конец описания функции
end interface
program vector
interface
real function norm(v) real, intent (in) :: v(3)
end function norm
end interface
real v(3) v=(/2,6,3/)
write(*,*) " Vector ",v
write(*,*) " Norm = ",norm(v)
end
real function norm(v) real v(3)
norm=sqrt(v(1)**2+v(2)**2+v(3)**2)
end function norm
Внешние подпрограммы можно размещать в отдельных файлах, а затем добавлять в проект.
Кроме того, разработанные процедуры можно откомпилировать отдельно и оформить в библиотеки.
5.Рекурсивные функции
Чтобы объявить процедуру рекурсивной, используется ключевое слово recursive.
Пример: программа печатает числа в обратном порядке. При этом рекурсивная подпрограмма оформлена как внутренняя.
program recurse
call info(5)
contains
recursive subroutine info(k)
if (k==0) then return
else
write(*,'(А,i1)’) " k = ", k;
call info(k-1)
end subroutine info
end
B некоторых случаях использование рекурсии позволяет легко реализовать вычислительный алгоритм.
Одним из таких алгоритмов является вычисление определителя.
program determinant
integer, parameter :: M=10 integer a(M,M)
write(*,*) "Enter matrix rows..."
do i=1,M read(*,*) a(i,1:M) end do
kk=det(a,M); write(*,*) "Determinant = ",kk
contains
integer recursive function det(A,N) result(s)
integer N, A(N,N), MR(N-1,N-1), k
MR=0; s=0
if (N==2) then S=A(1,1)*A(2,2)-A(1,2)*A(2,1)
else
do k=1,N
MR(1:k-1,1:N-1)=A(1:k-1,2:N)
MR(k:N-1,1:N-1)=A(k+1:N,2:N)
s=(-1)**(к+1)*A(k,1)*det(MR,N-1)+s
end do
end if
end function det
end