Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Родовые подпрограммы

Оператор и конструкция IF | Конструкция SELECT CASE | Упорядочение операторов | Уровни доступа | Спецификация входных-выходных параметров | Использование модуля | Внутренние процедуры | Связь с хозяином (Host) - глобальные данные | Объекты допустимого образа | Ввод-вывод без продвижения |


Читайте также:
  1. Вызов подпрограммы на ассемблере из программы на С
  2. Вызов подпрограммы на С из подпрограммы на ассемблере
  3. Массивы в качестве параметров подпрограммы
  4. Основные функции и подпрограммы
  5. Подпрограммы. Локальные и глобальные переменные
  6. Пример 4. Определение и использование модуля, родовые процедуры

 

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

Fortran 90 позволяет две или более процедуры сделать вызываемыми с одним и тем же генерируемым (родовым) именем. Таким образом может быть создано семейство (род) процедур с общим родовым именем. Вопрос о том, какая процедура будет вызвана, завист от типа данных (или рага) фактического параметра в операторе вызова. Подобный подход иллюстрирует набор встроенных функций.

Ввстроенная функцияя SQRT() (возвращает корень из ее аргумента) может быть дана для числовых типов real, double precision или complex:

•если фактический параметр число типа real, вызывается функция SQRT

• если фактический параметр число типа double precision, вызывается функция DSQRT

*если фактический параметр число типа complex, вызывается функция CSQRT

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

В Fortran 77 мы уже использовали элементарные родовые элементы, например, что вызов SIN(1.0) возвращает значение типа REAL, но вызов SIN(1.0D0) возвращает значение типа DOUBLE PRECISION. В Fortran 90 мы можем писать наши собственные функции или подпрограммы. Проиллюстрируем примером подпрограммы SWAP(A, B), которая меняет местами значения переменных A и B, используя различные подходящие подпрограммы, в зависимости от типа переменных: REAL, INTEGER или CHARACTER.

PROGRAM SWAP_MAIN

IMPLICIT NONE

INTEGER:: I, J, K, L

REAL:: A, B, X, Y

CHARACTER:: C, D, E, F

INTERFACE SWAP

SUBROUTINE SWAP_R(A, B)

REAL, INTENT (INOUT):: A, B

END SUBROUTINE SWAP_R

SUBROUTINE SWAP_I(A, B)

INTEGER, INTENT (INOUT):: A, B

END SUBROUTINE SWAP_I

SUBROUTINE SWAP_C(A, B)

CHARACTER, INTENT (INOUT):: A, B

END SUBROUTINE SWAP_C

END INTERFACE

 

I = 1; J = 2; K = 100; L = 200

A = 7.1; B = 10.9; X = 11.1; Y = 17.0

C = 'a'; D = 'b'; E = '1'; F = '"'

WRITE (*,*) I, J, K, L, A, B, X, Y, C, D, E, F

CALL SWAP(I, J); CALL SWAP(K, L)

CALL SWAP(A, B); CALL SWAP(X, Y)

CALL SWAP(C, D); CALL SWAP(E, F)

WRITE (*,*) I, J, K, L, A, B, X, Y, C, D, E, F

END

SUBROUTINE SWAP_R(A, B)

IMPLICIT NONE

REAL, INTENT (INOUT):: A, B

REAL:: TEMP

TEMP = A; A = B; B = TEMP

END SUBROUTINE SWAP_R

SUBROUTINE SWAP_I(A, B)

IMPLICIT NONE

INTEGER, INTENT (INOUT):: A, B

INTEGER:: TEMP

TEMP = A; A = B; B = TEMP

END SUBROUTINE SWAP_I

SUBROUTINE SWAP_C(A, B)

IMPLICIT NONE

CHARACTER, INTENT (INOUT):: A, B

CHARACTER:: TEMP

TEMP = A; A = B; B = TEMP

END SUBROUTINE SWAP_C

 

Этот интерфейс специфицирует три подпрограммы SWAP_I, SWAP_R и SWAP_C, которые можно вызвать по имени SWAP_R. Если параметры процедуры при вызове swap будут типа real, то будет вызвана SWAP_R; если параметры типа integer, то будет вызвана SWAP_I, если параметры типа CHARACTER то будет вызвана SWAP_C.

Вышеуказанное работает очень хорошо, но очень трудно отследить всю информацию, включающую это дерево различных вариантов для SWAP. Решение состоит в том, чтобы все, что связано с задачей SWAP поместить в одно место в модуле (module). Модуль может быть использован из главной программы с помощью оператора USE и указания имени модуля. Заметьте, что в INTERFACE для модуля, оператор спецификации MODULE PROCEDURE может быть использован для того, чтобы избегать того, что подпрограммы специфицированы и в части INTERFACE, и в части CONTAINS. Вы хотите иметь связь модуля и главной программы вместе в операторе

f90 part2.f90 part1.f90

 

Здесь модуль, он в файле part2.f90,

 

MODULE BO

INTERFACE SWAP

MODULE PROCEDURE SWAP_R, SWAP_I, SWAP_C

END INTERFACE

CONTAINS

SUBROUTINE SWAP_R(A, B)

IMPLICIT NONE

REAL, INTENT (INOUT):: A, B

REAL:: TEMP

TEMP = A; A = B; B = TEMP

END SUBROUTINE SWAP_R

SUBROUTINE SWAP_I(A, B)

IMPLICIT NONE

INTEGER, INTENT (INOUT):: A, B

INTEGER:: TEMP

TEMP = A; A = B; B = TEMP

END SUBROUTINE SWAP_I

SUBROUTINE SWAP_C(A, B)

IMPLICIT NONE

CHARACTER, INTENT (INOUT):: A, B

CHARACTER:: TEMP

TEMP = A; A = B; B = TEMP

END SUBROUTINE SWAP_C

END MODULE BO

 

Здесь главная программа, теперь свободна от всей неинтересующей информации о SWAP. Она в файле part1.f90.

 

PROGRAM SWAP_MAIN

USE BO

IMPLICIT NONE

INTEGER:: I, J, K, L

REAL:: A, B, X, Y

CHARACTER:: C, D, E, F

I = 1; J = 2; K = 100; L = 200

A = 7.1; B = 10.9; X = 11.1; Y = 17.0

C = 'a'; d = 'b'; E = '1'; F = '"'

WRITE (*,*) I, J, K, L, A, B, X, Y, C, D, E, F

CALL SWAP (I, J); CALL SWAP (K, L)

CALL SWAP (A, B); CALL SWAP (X, Y)

CALL SWAP (C, D); CALL SWAP (E, F)

WRITE (*,*) I, J, K, L, A, B, X, Y, C, D, E, F

END

 

Такой подход представлять вместе процедуры для одной операции с разными параметрами считается хорошей практикой.

Модули

Здесь рассмотрены разделы

Общая форма модуля PUBLIC и PRIVATE Определяющие операции
Глобальные данные Родовые процедуры Перегрузка присваивания
Процедуры модуля Перегрузка операций Область видимости

Модуль это тип программной единицы, введенный в Fortran 90. Он позволяет собрать определения, данные и процедуры, которые могут потребоваться другим программным единицам. Программа может использовать любое число модулей, ограничения определяются реализацией.

Общая форма модуля следующая:

MODULE имя

[определения]

...

[CONTAINS

процедуры модуля]

END [MODULE [имя]]

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

USE имя

в начале единицы.

 

Глобальные данные

Обычно переменные, объявленные в одной программной единице, не доступны из другой программной единицы (только главная ассоциация позволяет процедурам внутри программной единицы "разделять" переменные).

Использование модулей позволяет разместить объявления для всех глобальных переменных внутри модуля и использовать их через USE. Например:

 

MODULE global

REAL, DIMENSION(100):: a, b, c

INTEGER:: list(100)

LOGICAL:: test

END MODULE global

Все переменные в модуле глобальны и могут быть доступны программным единицам с помощью оператора:

USE global

Оператор USE должен появиться в начале программной единицы, непосредственно после PROGRAM или другого оператора программной единицы. Любое число модулей может быть использовано программной единицей и модули могут использовать другие модули. Тем не менее модули не могут использовать сами себя прямо или косвенно (модуль A использует A или модуль A использует модуль В, а модуль В использует модуль А).

Возможно ограничить список переменных в модуле, доступных программным единицам. Это может быть реализовано с помощью "защитных средств", гарантирующих программному модулю, что программная единица не может случайно изменить значения переменных в модуле. Требуется указать квалификатор ONLY:

USE global, ONLY: a, c

Это гарантирует, что программная единица может обращаться только к к переменным a и c из модуля global. Считается хорошей практикой программирования применять USE... ONLY для тех переменных, которые требуются программной единице.

Потенциальная проблема, возникающая с использованием переменных внутри модулей, это конфликты имен, когда одно и то же имя использовано для различных переменных в различных частях программы, Оператор USE может перекрыть их, допуская глобальные переменные вызывать через локальные имена, например:

 

USE global, state=>test

 

Здесь переменная state локальное имя для переменной test, находящейся в модуле global. Символ => связывает другое имя с глобальной переменной.

 

 

Процедуры модуля

Так жк как переменные объявлены в модуле глобальны, так жк процедуры, содержащиеся в нем становятся глобальными и могут быть вызваны из любой программной единицы с подходящим оператором USE. Процедуры, содержащиеся в модуле - это вызываемые процедуры модуля.

Процедуры модуля имеют ту же форму как и внешние процедуры - они могут содержать внутренние процедуцры. Тем не менее в отличие от и внешних процедур, они не нуждаются в проверке интерфейса при вызове программной единицы для процедур модуля - интерфейс к процедурам модуля явно предусмотрен через оператор USE.

Процедуры модуля вызываются как нормально (с помощью оператора CALL или вызова функции), но только теми программными единицами, которые имеют подходящий оператор USE. Процедуры модуля могут вызывать другие процедуры модуля, внутренние для того же модуля, или из других модулей (через оператор USE). Итак, процедуры модуля имеют доступ к переменным, объявленным в модуле через 'связь с хозяином'. Заметим, что как раз так, как с другими программными единицами, переменные, объявленные внутри процедур модуля, локальны для этих процедур и не могут быть вызваны прямо где-нибудь в другом месте..

Одно из главных, что использует для модуля, можно группировать вместе данные и любые связанные процедуры. Это практически полезно, когда вызваны производные типы данных и связанные с ними процедуры или операторы. Например:

MODULE cartesian

TYPE point

REAL:: x, y

END TYPE point

CONTAINS

SUBROUTINE swap(p1, p2)

TYPE(point), INTENT(INOUT):: p1

TYPE(point), INTENT(INOUT):: p2

TYPE(point):: tmp

tmp = p1

p1 = p2

p2 = tmp

END SUBROUTINE swap

END MODULE cartesian

 

Модуль carteasian содержит определения для типа данных, называемого point. Модуль cartesian также содержит подпрограмму модуля, которая заменяет значения своих аргументов типа point. Любая другая программная единица может объявить переменные типа point и использовать подпрограмму swap через оператор USE:

PROGRAM graph

USE cartesian

TYPE(point):: first, last

...

CALL swap(first, last)

...

END PROGRAM graph

 

 

PUBLIC и PRIVATE

По умолчанию все объекты в модуле доступны программным единицам через правильный оператор USE. Тем не менее иногда пишущий модуль может захотеть ограничить доступ к переменным, операторам определения или к процедурам модуля. Это делается с помощью комбинаций операторов PUBLIC и/или PRIVATE (или атрибутов).

Оператор/атрибут PRIVATE закрывают доступ к объектам модуля из любой программной единицы; PUBLIC дает противоположный эффект. Оба могут быть использованы по разному:

•Как оператор, PUBLIC и PRIVATE может установить умолчание для модуля или может относиься к списку переменных или имен процедур модуля

•Как атрибут, PUBLIC или PRIVATE могут управлять доступом к переменным в списке определения.

 

MODULE one

PRIVATE! установка умолчания для модуля

REAL, PUBLIC:: a

REAL:: b

PUBLIC:: init_a

CONTAINS

SUBROUTINE init_a()! public

...

SUBROUTINE init_b()! private

...

END MODULE one

 

Родовые процедуры

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

INTERFACE родовое_имя

MODULE PROCEDURE список_имен

END INTERFACE

 

Где список_имен есть процедуры для ссылок через родовое_имя.. Например, модуль содержит родовые подпрограммы для обмена значений двух массивов, включая массивы производных типов данных, будет выглядеть так:

MODULE cartesian

TYPE point

REAL:: x, y

END TYPE point

INTERFACE swap

MODULE PROCEDURE pointswap, iswap, rswap

END INTERFACE

CONTAINS

SUBROUTINE pointswap(a, b)

TYPE(point):: a, b

...

END SUBROUTINE pointswap

! подпрограммы iswap и rswap

END MODULE cartesian

Перегрузка операций

Способность вызывать одну из нескольких процедур через родовой интерфейс известна как перегрузка; существует родовое имя, которое перегружаемо. Какая именно процедура вызвана, зависит от параметров, передаваемых в вызывающем операторе. В простом случае для перегрузки имени процедуры, могут быть перегружены встроенные оперции (+, -, *, и т.д.). Таким образом обычно определяют результат известных операций для производных типов данных.

Перегрузку операций лучше всего определять в модуле, для чего требуется интерфейсный блок вида:

 

INTERFACE OPERATOR(операция)

код_интерфейса

END INTERFACE

 

Где операция это операция для перегрузки, код_интерфейса это функция с одним или двумя параметрами INTENT(IN). Например:

MODULE strings

INTERFACE OPERATOR (/)

MODULE PROCEDURE num

END INTERFACE

CONTAINS

INTEGER FUNCTION num(s, c)

CHARACTER(len=*), INTENT(IN):: s

CHARACTER, INTENT(IN):: c

num = 0

DO i=1,LEN(s)

IF(s(i:i)==c) num=num+1

END DO

END FUNCTION num

END MODULE strings

 

Обычно операция / не определена для символов и строк, но этот модуль strings содержит интерфейс и определяющую функцию, которая позволяет делить с помощью символа. Результатом операции является число повторений символа в строке:

USE strings

...

i = 'hello world'/'l'! i=3

i = 'hello world'/'o'! i=2

i = 'hello world'/'z'! i=0

 

Определение операций

Так же как возможна перегрузка существующих операций, можно определить и новые операции. Это необходимо в частности, когда манипулируют производными типами данных. Любые новые операции имеют форму имени, их результат определяется функцией. Так же как с перегрузкой операций определение функции требует блока интерфейса операции INTERFACE OPERATOR и должен быть один или два обязательных параметра INTENT(IN):

MODULE cartesian

TYPE point

REAL:: x, y

END TYPE point

INTEFACE OPERATOR (.DIST.)

MODULE PROCEDURE dist

END INTERFACE

CONTAINS

REAL FUNCTION dist(a, b)

TYPE(point) INTENT(IN):: a, b

dist = SQRT((a%x-b%x)**2 + (a%y-b%y)**2)

END FUNCTION dist

END MODULE cartesian

Эта операция.DIST. может быть использована для нахождения расстояния между двумя точками. Операция, которая определена только для типа данных point, не может быть использована для других типов данных. Так же как с перезагружаемыми операциями интерфейс и определяющая функция находятся в модуле. Имеет смысл хранить производный тип данных и соответствующие операции вместе..

Любая программная единица может использовать тип данных point и операцию.DIST., используя модуль cartesian:

USE cartesian

TYPE(point):: a, b

REAL:: distance

...

distance = a.DIST. b

 

Перегрузка операции присваивания

Возможна перегрузка смысла операции присваивания (=) для производных типов данных. Она опять же требует интерфейса - это определяющая подпрограмма (subroutine). Подпрограмма должна иметь два обязательных параметра: первый должен иметь INTENT(INOUT) или INTENT(OUT); второй должен иметь INTENT(IN). Например:

MODULE cartesian

TYPE point

REAL:: x, y

END TYPE point

INTEFACE ASSIGNMENT(=)

MODULE PROCEDURE max_point

END INTERFACE

CONTAINS

SUBROUTINE max_point(a, pt)

REAL, INTENT(OUT):: a

TYPE(point), INTENT(IN):: pt

a = MAX(pt%x, pt%y)

END SUBROUTINE max_point

END MODULE cartesian

Использование модуля cartesian позволяет программной единице присваивать значение типа point переменной типа real. Переменная типа real будет установлена в наибольшее значение из компонент переменной point. Например:

USE cartesian

TYPE(point):: a = point(1.7, 4.2)

REAL:: coord

...

coord = a! coord = 4.2

 

Область видимости

Единицы, входящие в область видимости (Scoping units)

Область видимости именованных объектов (переменных или процедур) это та часть программы, внутри которой имя или метка уникальны. Единица видимости это одно из следующих:

•Определение производного типа данных

•Интерфейсный блок, исключая любые определения производных типов данных, и интерфейсные блоки внутри него

•Программная единица или внутренняя процедура, исключая любые определения производных типов данных, и интерфейс.

Все переменные, типы данных, метки, имена процедур и т.д. внутри той же единицы видимости должны иметь различные имена. Объекты с одинаковыми именами, имеющие различные области видимости, позволяют отделить одно от другого.

Метки и имена

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

Область видимости имен (для переменных, скажем), объявленных в программной единице, от начала единицы до оператора END этой единицы. Область видимости имен, объявленных в главной программе или во внешней процедуре, распространяется на все внутренние процедуры без переопределенных внутренней процедурой. Область видимости имен, объявленных во внутренних процедурах, это только сами внутренние процедуры, не другие внутренние процедуры.

Область видимости имен, объявленных в модуле, распространяется на все программные единицы, которые используют этот модуль, за исключением тех внутренних процедур, где имя переопределено.

Имена программных единиц глобальны и поэтому должны быть уникальны. Имя программной единицы должно быть отличным от всех локальных объектов этой единицы. Имя внутренней процедуры имеет силу в содержимом программной единицы. Поэтому все внутренние процедуры внутри одной программной единицы должны иметь различные имена.

Следующий пример показывает видимость в единице:

MODULE scope1! область 1

...! область 1

CONTAINS! область 1

SUBROUTINE scope2()! область 2

TYPE scope3! область 3

...! область 3

END TYPE scope3! область 3

INTERFACE! область 3

...! область 4

END INTERFACE! область 3

REAL:: a, b! область 3

10...! область 3

CONTAINS! область 2

FUNCTION scope5()! область 5

REAL:: b! область 5

b = a+1! область 5

10...! область 5

END FUNCTION! область 5

END SUBROUTINE! область 2

END MODULE! область 1

Ввод-вывод


Дата добавления: 2015-11-14; просмотров: 76 | Нарушение авторских прав


<== предыдущая страница | следующая страница ==>
Процедуры в качестве параметров| Вещественные - форма с фиксированной точкой

mybiblioteka.su - 2015-2024 год. (0.041 сек.)