современный фортран , Бортеньев. О. В. Бартеньев Современный Фортран
Скачать 2.24 Mb.
|
279 О. В. Бартеньев. Современный ФОРТРАН subroutine shosa( ) real da, a, dum common /bz/ da, a, dum(10) real(8), save :: x, y save /bz/ 8.23. Атрибут STATIC Имеющие атрибут STATIC переменные (в процедуре или модуле) сохраняются в (статической) памяти в течение всего времени выполнения программы. Атрибут является расширением над стандартом Фортрана и эквивалентен ранее приведенному атрибуту SAVE и атрибуту STATIC языка СИ. Значения статических переменных сохраняются после выполнения оператора RETURN или END. Напомним, что в CVF и FPS по умолчанию переменные (кроме динамических) размещены в статической памяти. Для изменения правил умолчания используются атрибуты ALLOCATABLE, AUTOMATIC и POINTER. Атрибут STATIC может быть задан в двух формах: STATIC [[::] список объектов] type-spec, STATIC [, атрибуты] :: список объектов Список объектов может включать имена переменных и common-блоков. Имена последних при включении их в список объектов оператора STATIC обрамляются слешами. Пример: integer :: ng = -1 do while(ng /= 0) ! Цикл завершается при ng = 0 call sub1(ng, ng + ng) print *, 'Enter integer non zero value to continue or zero to quit' read *, ng end do contains subroutine sub1(iold, inew) integer, intent(inout) :: iold integer, static :: n2 ! При каждом вызове n2 = -1 integer, automatic :: n3 integer, intent(in) :: inew if(iold == -1) then n2 = iold n3 = iold ! Значение n3 определено только при ng = -1 end if print *, 'new: ', inew, ' n2: ', n2, ' n3: ', n3 end subroutine end 280 8. Программные единицы 8.24. Атрибут VOLATILE Атрибут может быть задан только в CVF и является расширением над стандартом Фортрана. Атрибут указывает компилятору, что значение объекта непредсказуемо. Объект, обладающий атрибутом VOLATILE, не будет оптимизироваться в процесс компиляции. Как и другие, атрибут может быть задан в двух формах: VOLATILE список объектов type-spec, VOLATILE [, атрибуты] :: список объектов Список объектов может включать имена переменных и common-блоков. Имена последних при включении их в список объектов оператора VOLA- TILE обрамляются слешами. Переменная или common-блок должны объявляться VOLATILE, если способ их определения неочевиден для компилятора. Например, если операционная система размещает переменную в разделяемой памяти, с тем чтобы ее могла использовать другая программа, которая может в том числе и изменить значение переменной, или в случае ассоциирования памяти посредством оператора EQUIVALENCE. Если составной объект (массив, производный тип) объявляется VOLA- TILE, то каждый его элемент получает этот атрибут. Аналогично если com- mon-блок объявляется VOLATILE, то этим атрибутом обладает каждый его элемент. Атрибут VOLATILE не может быть задан процедуре, результату функции и namelist-группе. Пример: logical(kind = 1) ipi(4) integer(kind = 4) a, b, c, d, e, ilook integer(kind = 4) p1, p2, p3, p4 common /blk1/ a, b, c volatile /blk1/, d, e equivalence(ilook, ipi) equivalence(a, p1) equivalence(p1, p4) Именованный common-блок blk1, переменные d и e объявляются VOLA- TILE явно. Поведение переменных p1 и p4 в результате их ассоциирования по памяти (прямого и непрямого) с volatile-переменной a зависит от a. 8.25. Чистые процедуры Чистыми называются процедуры, не имеющие побочных эффектов. Пример побочного эффекта демонстрирует следующая программа: program side_effect real(4) :: dist, d, p = 3.0, q = 4.0, r = 5.0 281 О. В. Бартеньев. Современный ФОРТРАН d = max(dist(p, q), dist(q, r)) print *, d ! 7.071068 end program side_effect function dist(p, q) real(4) :: dist, p, q dist = sqrt(p * p + q * q) q = dist ! Изменение q - побочный эффект end function dist Суть его в том, что функция dist переопределяет значение параметра q. А это означает, что второй вызов функции dist при вычислении d, выполняется при q, равном 5.0, вместо ожидаемого первоначального значения q = 4.0. Такие эффекты запрещены стандартом и должны отслеживаться и устраняться программистом. Сообщение о том, что процедура является чистой, обеспечивается ключевым словом PURE, применяемым в заголовке процедуры: [type-spec] PURE SUBROUTINE | FUNCTION name & [RESULT (resultname)] или PURE [type-spec] SUBROUTINE | FUNCTION name & [RESULT (resultname)] type-spec - тип результирующей переменной функции. name - имя процедуры. resultname - имя результирующей переменной функции. Чистая процедура характеризуется тем, что: • функция возвращает значение и не меняет ни одного из своих параметров; • подпрограмма изменяет только те параметры, которые имеют вид связи INTENT(OUT) и INTENT(INOUT). По умолчанию чистыми являются: • все встроенные функции и встроенная подпрограмма MVBITS; • процедуры библиотеки высокоскоростного Фортрана, применяемого для параллельных вычислений под Юниксом. В чистых процедурах все формальные параметры, кроме формальных процедур и ссылок, должны иметь вид связи: • для функций - только INTENT(IN); • для подпрограмм - любой: INTENT(IN, или OUT, или INOUT). Никакие локальные переменные чистой процедуры, в том числе и относящиеся к внутренним процедурам, не должны: • обладать атрибутом SAVE; • быть инициализированными в операторах объявления или DATA. В чистых процедурах имеются ограничения на использование: 282 8. Программные единицы • глобальных переменных; • формальных параметров с видом связи INTENT(IN) или с необъявленным видом связи; • объектов, ассоциируемых по памяти с какими-либо глобальными переменными. Ограничения таковы: перечисленные объекты не должны использоваться: 1) в случаях, когда возможно изменение их значения. Это может произойти, если переменная является: • левой частью оператора присваивания или прикрепления ссылки (если объект является ссылкой); • фактическим параметром, ассоциированным с формальным параметром с видом связи INTENT(OUT или INOUT) или обладающим атрибутом POINTER; • индексной переменной операторов DO, FORALL или встроенного DO- цикла; • переменной оператора ASSIGN; • элементом списка ввода оператора READ; • именем внутреннего файла оператора WRITE; • объектом операторов ALLOCATE, DEALLOCATE или NULLIFY; • спецификатором IOSTAT или SIZE операторов В/В или STAT операторов ALLOCATE и DEALLOCATE; 2) в создании ссылки, например в качестве адресата или в качестве элемента правой части оператора присваивания переменной производного типа, если он имеет ссылочный компонент на любом из его уровней. Чистые процедуры не должны содержать: • операторы В/В во внешние файлы или устройства; • операторы PAUSE и STOP. Чистые процедуры предназначены для вызова в тех случаях, когда вызов иных, не владеющих ключевым словом PURE процедур недопустим: • в операторе FORALL или его выражении-маске; • из другой чистой процедуры. Также только чистую процедуру можно использовать в качестве параметра другой чистой процедуры. Если чистая процедура используется в приведенных ситуациях, то ее интерфейс должен быть задан явно и она должна быть объявлена в нем с ключевым словом PURE. Напомним, что все встроенные процедуры являются чистыми и по умолчанию обладают явным интерфейсом. Пример: pure function decr(k, m) real(4) :: decr 283 О. В. Бартеньев. Современный ФОРТРАН integer(4), intent(in) :: k, m ! Формальные параметры чистой функции decr = real(m) / real(k) ! должны иметь вид связи INTENT(IN) end function decr program pudem real(4), dimension(5, 5) :: array = 5.0 interface pure function decr(k, m) ! Поскольку функция используется в FORALL, real(4) :: decr ! то необходимо задать ее интерфейс integer(4), intent(in) :: k, m end function decr end interface forall(i = 1:5, j = 1:5) array(i, j) = decr(i, j) print '(10f5.1)', array(1, :) ! 1.0 2.0 3.0 4.0 5.0 end program pudem Замечание. Чистые процедуры введены стандартом 1995 г. 8.26. Элементные процедуры Элементные пользовательские процедуры подобно встроенным элементным процедурам могут иметь в качестве фактических параметров либо скаляры, либо массивы. В последнем случае массивы должны быть согласованы, т. е. иметь одинаковую форму; результатом процедуры является поэлементная обработка массивов - фактических параметров. Приведем пример выполнения встроенной элементной функции MOD, возвращающей остаток от деления первого параметра на второй: integer(4), dimension(5) :: a = (/ 1, 2, 3, 4, 5 /), b = (/ 1, 2, -2, 4, 3 /), c integer(4) :: d c = mod(a, b) ! Параметры функции - массивы print *, c ! 0 0 1 0 2 d = mod(b(4), a(3)) ! Параметры функции - скаляры print *, d ! 1 Программная единица, вызывающая элементную функцию, должна содержать ее интерфейс, в котором явно указано слово ELEMENTAL. Цель введения элементных функций - упростить распараллеливание вычислений на многопроцессорных машинах: компилятор, имеющий сведения о том, что функция элементная, выполняет распараллеливание по заложенным в него правилам. Элементные функции - это чистые функции, имеющие только скалярные формальные параметры, не являющиеся ссылками или процедурами. Вид связи параметров - INTENT(IN). Результирующая переменная элементной функции также является скаляром и не может быть ссылкой. Элементная функция снабжается ключевым словом ELEMENTAL, которое автоматически подразумевает ключевое слово PURE. Элементные функции не могут быть оснащены ключевым словом RECURSIVE. 284 8. Программные единицы Если фактическими параметрами элементной функции являются массивы, то они должны быть согласованы; результатом такой функции является массив, согласованный с массивами-параметрами. Пример: elemental integer(4) function find_c(a, b) integer(4), intent(in) :: a, b ! Не забываем задать вид связи INTENT(IN) if(a > b) then find_c = a else if(b < 0) then find_c = abs(b) else find_c = 0 end if end function find_c program etest interface ! Интерфейс обязателен elemental integer(4) function find_c(a, b) integer(4), intent(in) :: a, b ! Обязательное задание вида связи INTENT(IN) end function find_c end interface integer(4), dimension(5) :: a = (/ -1, 2, -3, 4, 5 /), b = (/ 1, 2, -2, 4, 3 /), c integer(4) :: d = 5 c = find_c(a, b) ! Параметры функции - массивы print *, c ! 0 0 2 0 5 d = find_c(-1, 1) ! Параметры функции - скаляры print *, d ! 0 end program etest Замечание. Поскольку элементные функции являются чистыми, они могут быть использованы в операторе и конструкции FORALL. Элементные подпрограммы задаются подобно элементным функциям. В теле процедуры могут изменяться параметры с видом связи OUT и INOUT. Пример: elementalsubroutine find_c(a, b, c) integer(4), intent(in) :: a, b integer(4), intent(out) :: c if(a > b) then c = a else if(b < 0) then c = abs(b) else c = 0 285 О. В. Бартеньев. Современный ФОРТРАН end if end subroutine find_c program etest2 interface elemental subroutine find_c(a, b, c) integer(4), intent(in) :: a, b integer(4), intent(out) :: c end subroutine end interface integer(4), dimension(5) :: a = (/ -1, 2, -3, 4, 5 /), b = (/ 1, 2, -2, 4, 3 /), c integer(4) :: d = 5 286 8. Программные единицы call find_c(a, b, c) ! Параметры и результат - массивы print *, c ! 0 0 2 0 5 call find_c(-1, 1, d) ! Параметры и результат - скаляры print *, d ! 0 end program etest2 Замечание. Элементные процедуры введены стандартом 1995 г. 8.27. Операторные функции Если некоторое выражение встречается в программной единице неоднократно, то его можно оформить в виде операторной функции и заменить все вхождения выражения на эту функцию. Операторные функции задаются так: имя функции ([список формальных параметров]) = выражение Если список формальных параметров содержит более одного имени, то имена разделяются запятыми. Как и встроенная или внешняя функция, операторная функция вызывается в выражении. Областью видимости операторной функции является программная единица, в которой эта функция определена. В то же время операторная функция может быть доступна в других программных единицах за счет ассоциирования через носитель или use-ассоциирования, но не может быть ассоциирована через параметры процедуры. Тип операторной функции следует объявлять явно, размещая ее имя в операторе объявления типа или в операторе IMPLICIT. Пример. Выполнить табуляцию функции z = siny * e -x real(8) :: x = -1.0_8, y, z ! Используем двойную точность real(8) :: dx = 0.4_8, dy = 0.3_8 z(x, y) = exp(-x) * sin(y) ! Задание операторной функции z(x, y) write(*, '(6h x/y , 20f8.2)') (y, y = -0.6, 0.6, 0.3) do while(x <= 1.0_8) write(*, '(f6.2 \)') x ! Вывод x без перехода на новую строку y = -0.60_8 do while(y <= 0.6_8) write(*, '(f8.2 \)') z(x, y) ! Вывод z без перехода на новую строку y = y + dy end do x = x + dx write(*, *) ! Переход на новую строку end do end Замечание. Для вывода без продвижения на новую строку используется преобразование обратного слеша (\). 287 О. В. Бартеньев. Современный ФОРТРАН 8.28. Строка INCLUDE В больших программах исходный код целесообразно хранить в разных файлах. Это упрощает работу над фрагментами программы и над программой в целом. Включение исходного кода одного файла в код другого можно выполнить при помощи директивы $INCLUDE или строки INCLUDE, имеющей вид: INCLUDE 'имя файла' имя файла - заключенное в апострофы или двойные кавычки имя текстового файла с исходным кодом фрагмента Фортран-программы. При необходимости имя файла должно содержать и путь к файлу. Строка INCLUDE не является оператором Фортрана. Она вставляет содержимое текстового файла в то место программной единицы, где он расположен. При этом строка INCLUDE замещается вставляемым текстом. Компилятор рассматривает содержимое вставленного файла как часть исходной программы и выполняет компиляцию этой части сразу после ее вставки. После завершения компиляции вставленного файла компилятор продолжает компиляцию исходной программной единицы начиная с оператора, следующего сразу после строки INCLUDE. Включаемый файл может содержать другие строки INCLUDE, но не должен прямо или косвенно ссылаться сам на себя. Такие включаемые файлы называются вложенными. Компилятор позволяет создавать вложенные включаемые файлы, содержащие до 10 уровней вложения с любым набором строк INCLUDE. Первая строка включаемого файла не должна быть строкой продолжения, а его последняя строка не должна содержать перенос. Перед оператором не может быть поставлена метка. В Фортране include-файлы рассматриваются как избыточное средство языка и могут быть практически полностью и с большим эффектом заменены модулями. Модули не только обеспечивают доступ к расположенным в модуле операторам объявления и описания и размещенным после оператора CONTAINS модульным процедурам, но и позволяют выполнять (за счет use-ассоциирования) обмен данными между использующими модули программными единицами. 8.29. Порядок операторов и директив Операторы и директивы в программных единицах должны появляться в приведенном в табл. 8.5 порядке. 288 8. Программные единицы Таблица 8.5. Последовательность операторов и директив $INTEGER, $REAL, $[NO]SRICT, $OPTIMIZE $ATTRIBUTES BLOCK DATA, FUNCTION, MODULE, PROGRAM, SUBROUTINE $[NO]DEBUG USE-операторы $[NO]DECLARE IMPLICIT NONE PARAMETER $DEFINE, $UNDEFINE IMPLICIT $IF, $IF DEFINED Определения производных типов $ELSE, $ELSEIF, $END IF Интерфейсные блоки $FIXFORMLINESIZE Операторы объявления типа PARAMETER ENTRY $[NO]FREEFORM Операторы объявления DATA FORMAT $INCLUDE, $LINE Операторные функции $LINESIZE, $[NO]LIST Исполняемые операторы DATA $MESSAGE CONTAINS $OBJCOMMENT, $PACK Внутренние и модульные процедуры $PAGE, $PAGESIZE END $SUBTITLE, $TITLE В табл. 8.6 для разных программных компонентов указаны операторы, которые могут в них появляться. Строка "Объявления" подразумевает операторы PARAMETER, IMPLICIT, объявления типов данных и их атрибутов. Таблица 8.6. Операторы программных компонентов Операторы Главная программа Модуль BLOCK DATA Внешняя процедура Модульная процедура Внутренняя процедура Тело интерфейса USE Да Да Да Да Да Да Да ENTRY Нет Нет Нет Да Да Нет Нет FORMAT Да Нет Нет Да Да Да Нет Объявления Да Да Да Да Да Да Да DATA Да Да Да Да Да Да Нет Определения производных типов Да Да Да Да Да Да Да Интерфейсные блоки Да Да Нет Да Да Да Да Операторные функции Да Нет Нет Да Да Да Нет Исполняемые операторы Да Нет Нет Да Да Да Нет CONTAINS Да Да Нет Да Да Нет Нет 289 9. Форматный ввод/вывод Данные в памяти ЭВМ хранятся в двоичной форме, представляя собой последовательность нулей и единиц. С особенностями представления различных типов данных в ЭВМ можно познакомиться, например, в [5]. Употребляемые в Фортране модели данных целого и вещественного типа рассмотрены в разд. 6.11.1. Входные и выходные данные часто необходимо представить в ином, отличном от внутреннего представления виде. Тогда и возникает задача преобразования данных из входной формы в машинное (внутреннее) представление и, наоборот, из машинного представления во внешнее, например текстовое или графическое. Стандартные средства Фортрана поддерживают 4 вида В/В данных: • форматный; • под управлением списка В/В; • неформатный; • двоичный. Первые два вида предназначены для преобразования текстовой информации во внутреннее представление при вводе и, наоборот, из внутреннего представления в текстовое при выводе. Выполняемые преобразования при форматном В/В задаются списком дескрипторов преобразований. Управляемый списком В/В по существу является разновидностью форматного В/В: преобразования выполняются по встроенным в Фортран правилам в соответствии с типами и значениями элементов списка В/В. Управляющий передачей данных список может быть именованным или неименованным. В настоящей главе мы рассмотрим только два первых вида передачи данных: форматный и под управлением списка. Неформатный и двоичный В/В рассмотрены в гл. 10. |