Fortran 90 ANSI Standard Fortran 90 является развитием языка Fortran 77, так что программа, написанная на Fortran 77, мо...
17 downloads
556 Views
1MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Fortran 90 ANSI Standard Fortran 90 является развитием языка Fortran 77, так что программа, написанная на Fortran 77, может быть скомпилирована и запущена как программа на Fortran 90. Стандарт Fortran 90 Standard вводит много новых средств для операций над массивами, новые методы спецификации точности, свободный формат исходного кода, рекурсию, динамические массивы и т.д.. Несмотря на то, что весь Fortran 77 включен в Fortran 90, новый стандарт ANSI предполагает, что некоторые средства из Fortran 77 'потеряют ценность'. Потеряют ценность средства, подобные классифицируемым как 'устаревшие' в последовательности ревизий и переносимые в будущие версии языка Fortran. Программы на текущем языке стандарта Fortran 77 могут успешно компилироваться компиляторами Fortran 90 без каких-либо изменений. Тем не менее структура программы на языке Fortran 90 может значительно отличаться от структуры эквивалентной программы на Fortran 77. Программист должен остерегаться смешения двух стилей. В такой же мере компилятор с Fortran 90 требует обеспечения объяснений для сообщений о некоторых кодах несоответствия (несогласования), то есть использование операторов или переменных, которые разрешены сверх множества правил, выходящих за пределы стандарта. Это поможет программисту написать правильный код. Как уже упомянуто, Fortran 90 добавляет к Fortran 77 небольшими средствами преимущества новых вычислений , в частности такого развития как новых важных динамических структур данных введения в параллельные архитектуры Объектно-ориентированные средства Fortran 90 имеет такие Объектно-ориентированные средства как: •абстрактные типы данных -- типы, определенные пользователем; •закрытие данных -- атрибуты PRIVATE и PUBLIC ; •инкапсуляция -- средства модулей и закрытых данных; •наследование и расширение – супертипы, оператор перекрытия и родовые процедуры; •полиморфизм -- пользователь может перекрывать ее с помощью перегрузки родовых программ; •повторное использование - через модули;
Краткий обзор наиболее важных средств Fortran 90 (материал взят из http://www.lahey.com/other.htm).
проекта
Lahey:
http://www.lahey.com/index.html,
Средства расширения языка
75 новых встроенных функций:
Структуры и производные типы
Манипуляции над битовыми
Указатели
Строковые переменные
Объявления свойств Семейство типов
Модули и процедуры
Конструкторы
Интерфейсы
Управление программой
Внутренние процедуры
DO / END DO
Рекурсия
DO WHILE
Необязательные параметры и ключевые слова
Конструкция CASE Другие средства CYCLE, EXIT Стандартная форма исходного текста Массивы Свободная форма исходного текста Управление векторами и матрицами Длинные имена Выражения для массивов Включение файлов Распределяемые (динамические) массивы Операции отношения Автоматические массивы Комментарий в конце подпрограммы Секция (вырезка) массива Fortran 90 в Lahey Конструкция WHERE
Возможности расширения языка Новый язык содержит возможности для пользователя расширять его собственными понятиями, например, для интервальной арифметики, арифметики рациональных или динамических символьных строк. Путем определения новых типов данных или операторов, и перегружаемых операций и процедур (так что вы можете использовать плюс + как символ сложения интервалов, а не только для обычных чисел), мы можем создать пакет (модуль) без использования препроцессора. Мы можем в скором времени ожидать число расширений для различных приложений в форме модулей от различных производителей. Некоторые уже доступны в NAG. Данные Структуры (производные типы) В Фортран раньше не разрешалось использование некоторых типов, определенных пользователем. Теперь это можно делать. Вы можете группировать Ваши данные, используя производные типы. При этом можно комбинировать встроенные типы (включая массивы и указатели). Доступ к компонентам новых типов осуществляется использованием уточнителя, а в качестве разделителя в нем знак процента. (Производные типы известны в VAX Fortran как записи.) Ниже приведен пример TYPE staff_member CHARACTER(LEN=20) :: first_name, last_name INTEGER :: identification, department END TYPE который может быть использован для того, чтобы описывать отдельные единицы. Комбинация индивидуальных единиц может создана таким образом TYPE(staff_member), DIMENSION(100) :: staff Отдельная единица может быть вызвана как staff(number), а поле может быть вызвано как staff(number)%first_name. Вы можете образовать гнездо определений TYPE company CHARACTER(LEN=20) :: company_name TYPE(staff_member), DIMENSION(100) :: staff END TYPE : TYPE(company), DIMENSION(10) :: several_companies Значительно более интересный пример - это разреженная матрица A, имеющаяс более сотни ненулевых элементов, которая может быть специфицирована следующим оператором
TYPE NONZERO REAL VALUE INTEGER ROW, COLUMN END TYPE и TYPE (NONZERO) :: A(100) Вы тогда получаете значение A(10), записав A(10)%VALUE. Присваивание может быть записано как A(15) = NONZERO(17.0,3,7) Для того, чтобы использовать в COMMON определенные пользователем в примере типы, или чтобы безусловно сделать так, чтобы два типа данных, которые выглядят как один и тот же, рассмотривались как идентичные, вы можете использовать оператор SEQUENCE, в последнем случае требуется , чтобы переменная была специфицирована как PRIVATE.
! Еще один пример, использующий производные типы и модули module pipedef type pipe ! определяется новый тип 'pipe', который real diameter ! включает две компоненты типа reals, real flowrate ! компоненту типа integer и компоненту типа character. integer length character(len=10) :: flowtype end type pipe end module pipedef
program main use pipedef ! Связывает с модулем pipedef программу main. type(pipe) water1, gas1 ! объявляет две переменные типа 'pipe'. water1 = pipe(4.5,44.8,1200,"turbulent") ! присваивает значение переменной water1.
gas1%diameter = 14.9 ! присваивает значение gas1%flowrate = 91.284 ! частям переменной gas1. gas1%length = 2550 gas1%flowtype = 'laminar' . . end program з Указатели Указатели позволяют программе вызвать более одной области памяти с одним именем. По сравнению с указателями языка С указатели в Fortran 90 более ограничены, и поэтому легче для отладки и оптимизации. Объекты данных с атрибутами-указателями могут иметь ассоциированную с ними память, выделяемую и возвращаемую, которую можно использовать динамически. Указатели могут быть ассоциированы с другими указателями и с другими объектами, облегчая работу с такими структурами данных как списки и деревья. з Вид семейства Тип семейства дает возможность пользователю запросить, какой встроеннный тип использован базовым для точности и диапазона значений. Это средства среду вычислений. Программисты, которые адресуют свои программы разным машинам, должны иметь дело с различным числом точных цифр. Используя семейство (kind), программист может специфицировать требуемую числовую точность. ! Пример, использующий атрибут KIND и функцию KIND . ! Этот пример потребует от компилятора символьный тип данных kind(2) ! Это допустимо в F90, но не обязательно. program main integer, parameter :: kanji_char_kind = 2 ! Используем атрибут kind, чтобы определить известный kind. character(kind = kanji_char_kind, len=20), dimension(20) :: name
! Используем функцию kind, чтобы определить kind из объекта известного kind. real(kind = kind(0.0d0)) :: pi=3.14159265359d0 open(unit=1, file='names', form='formatted', access='sequential') read(1,10) (name(i), i=1,20) write(*,20) name write(*,30) pi close(unit=1) 10 format(a20) 20 format(1x,t7,a20,t30,a20,t55,a20) 30 format(1x,1p,d18.11) end program з Объявления (декларации, спецификации) Объекты данных могут иметь комбинацию 12 различных атрибутов, таких как POINTER, SAVE, и т.д. Fortran 90 вводит "объектно-ориентированные " объявления, в которых объекты с одинаковыми комбинациями атрибутов могли быть декларированы со всеми их атрибутами в одном операторе. В одной строке могут теперь быть записаны: REAL, DIMENSION (3), PARAMETER :: & a = (/ 0.0, 0.0, 0.0 /), b = (/ 1.0, 1.0, 1.0 /) COMPLEX, DIMENSION(10) :: john Тогда как переменные a и b задают константный вектор из 3 элементов и значения с плавающей точкой 0.0 и 1.0, соответственно, в то время как john задается как вектор типа complex из 10 элементов типа complex, все еще не присвоены значения ни одной переменной. Двойная точность может быть реализована более общим методом, чтобы дать настраиваемую точность, даваемую параметром KIND, для которого точность мы определим полностью для всех типов переменных. INTEGER, PARAMETER :: LP = SELECTED_REAL_KIND(20) REAL (KIND = LP) :: X, Y, Z
Выше приведенные операторы объявляют переменные X, Y и Z как REAL с плавающей точкой с числом десятичных цифр, меньшим 20 с типом данных, называемым LP (где LP стандартное сокращение для LONG PRECISION). з Конструкторы Массив и объекты производных типов данных могут быть инициализированы в операторе объявления. Конструктор может быть использован как константа там, где ожидается массив или производный тип.
IMPLICIT NONE*
Если Вы хотите использовать алгол-принцип спецификации всех переменных, это достигается командой IMPLICIT NONE с явными описаниями типа. Эта стандартизация популярного расширения F77 делает обязательным для всех объектов данных явное объявление. з Управление программой
Программа на FORTRAN 77 была часто спроектирована по блок-схеме. Этот вид проектирования ведет естественно к коду, составленному из фрагментов проверки и ветвления программ, он будет труден для внесения изменений, в частности для увеличения размера программы, он потребует большой отладки и в нем будет очень мало повторного использования. Новые методы проектирования программ включают декомпозицию больших проблем на малые проблемы до тех пор, пока решения не станут тривиальными. Новый стандарт Fortran 90 предоставляет конструкции управления, которые делают более легкими реализацию новых методов проектирования программного обеспечения. В язык включены три новые конструкции для записи цикла. з DO / END DO* END DO заменяет традиционный помеченный оператор завершения CONTINUE для цикла DO. При этом метка в операторе DO не обязательна.
Ниже приводится пример бесконечного цикла, который может быть закончен условным оператором GOTO. name: DO выполняемые операторы END DO name
Обычный оператор цикла DO имеет следующую новую упрощенную форму без номера оператора, name: DO i = целое_выраж_1, целое_выраж_2 , целое_выраж_3 выполняемые операторы END DO name где i так называемая управляющая переменная, и где целое_выраж_3 необязательно. з DO WHILE*
Оператор обеспечивает повторение блока до тех пор, пока условие верно. name: DO WHILE (логическое_выражение) выполняемые операторы END DO name Имя здесь не обязательно, но может быть использовано для следующих циклов в целях индикации, когда нужны итерации, с операторами завершения CYCLE или EXIT. S1: DO IF (X Y ) THEN Z=X EXIT S1 END IF CALL NEW(X) END DO N=0
LOOP1: DO I = 1, 10 J= I LOOP2: DO K =1, 5 L=K N = N +1 END DO LOOP2 END DO LOOP1
В последнем случае конечные значения переменных будут следующими, в полном соответствии со стандартом, I = 11, J = 10, K = 6, L = 5, и N = 50. Именовать цикл не обязательно. Заметим, что этот тип имени ограничивает конструкции цикла DO, CASE или IF...THEN...ELSE...ENDIF. Старые возможности с номерами операторов все еще допустимы , также и в свободной форме. з Конструкция CASE *
Заменяет запутанную конструкцию IF/ELSE IF или вычисляемые GO TO. Делает код более легким для понимания и для записи. Новая команда такова SELECT CASE (выражение) CASE блок-переключатель блок CASE блок-переключатель блок CASE DEFAULT блок по умолчанию END SELECT Типичная конструкция: SELECT CASE(3*I-J) ! управляющая переменная это 3*i-j CASE(0) ! для значения нуль : ! вы вычисляете код здесь
CASE(2,4:7) ! для значений 2, 4, 5, 6, 7 : ! вы вычисляете код здесь CASE DEFAULT ! и для всех остальных значений ! вы вычисляете код здесь : ! END SELECT Если CASE DEFAULT отсутствует и не находится альтернатива, вычисление продолжается прямо с оператора, за END SELECT без выдачи сообщения об ошибке. Другой пример: INTEGER FUNCTION SIGNUM(N) SELECT CASE (N) CASE (:-1) SIGNUM = -1 CASE (0) SIGNUM = 0 CASE (1:) SIGNUM = 1 END SELECT END
! Еще один пример использования конструкции CASE ! (c)Copyright 1993, Lahey Computer Systems, Inc. All RIGHTS RESERVED ! Test: case1 ! ! Legal - все возможные типы case-значение-диапазон для integer*4 ! integer*4 i integer n /9/, a (9) /1,-11,81,31,41,51,61,71,25/ do 50 j = 1, n i = a (j) select case ( i ) case (1) print*,"should be 1, is ",i
case ( : -11 ) print*,"should be 11 or less, is ",i case ( 81 : ) print*,"should be 81 or greater, is ",i case ( 31 : 32 ) print*,"should be 31 or 32, is ",i case ( 41, 50:51, 61 :61, 71:72 ) print*,"should be 41, 50, 51, 61, 71, or 72, is ",i case default print,"should be any other number, is ",i end select 50 continue end з CYCLE, EXIT*
Прерывает отдельную итерацию цикла DO или весь цикл прежде чем завершится повторяемый блок. Уменьшает потребность в GO TO. з Конструируирование имен Names
Вы можете конструировать метки для IF, DO, и CASE, чтобы сделать код более читаемым и возможными CYCLE или EXIT к внешним уровням следующих циклов. з Массивы
Управление векторами и матрицами Это одна из самых важных частей нового стандарта. Определение массива порождает образ (shape), задаваемый числом его измерений (рангом) и протяженностью по каждому измерению. Два массива соответствуют друг другу, если они имеют один и тот же образ. Операции как правило поэлементные. Ранг массива не есть ранг матрицы в математическом смысле! REAL, DIMENSION(5,20) :: x, y REAL, DIMENSION(-2:2,20) :: z : z = 4.0*y*sgrt(x) REAL, DIMENSION(6) :: B REAL, DIMENSION(2,3) :: C B = (/ 1, 1, 2, 3, 5, 8 /) C = RESHAPE( B, (/ 2,3 /) ) где первый аргумент встроенной функции RESHAPE дает значение, а второй аргумент дает новый образ. Два дополнительных, но необязательных аргумента возможны у этой функции. Вышеприведенное можно записать более кратко с помощью атрибута PARAMETER. В первой строке ниже атрибут PARAMETER принудительный (если присваивание может быть выполнено в этой самой строке), но во второй строке он не обязателен. Атрибут PARAMETER означает, что неизвестное может не быть заменено во время выполнения программы. REAL, DIMENSION(6), PARAMETER :: B = (/ 11, 12, 13, 14, 15, 16 /) REAL, DIMENSION(2,3), PARAMETER :: C = RESHAPE( B, (/ 2, 3 /) ) Некоторые операторы для реальных параллельных вычислений не включены в Fortran 90. См. HPF. з Выражения для массивов
Компилируемый цикл DO может быть теперь свернут в один оператор. Используем целиком массивы в выражениях или как аргументы встроенных функций.
! Пример программы, использующей атрибуты внутри типа stmt, и выражение для массива.
program main ! Объявляем dim1 как parameter, со значением 5. integer, parameter :: dim1=5 ! Объявляем res, x, & y как 5-элементные массивы, и инициализируем x & y. real, dimension(dim1) :: res, x=(/1.,2.,3.,4.,5./),y=(/5.,4.,3.,2.,1./) ! Прибавим соответствующие элементы из x & y, и присвоим res. res = x + y ! Все элементы из res будут равны 6. write(*,10) res 10 format(1x,5(f3.0,4x)) end program з Распределяемые (динамические) массивы *
Память для массивов может распределяться во время выполнения. Fortran 90 содержит четыре различных способа для обеспечения динамического доступа. Первый путь - использование указателей для вектора и матрицы. Второй способ - использование распределяемого массива, то есть с помощью операторов ALLOCATE и DEALLOCATE вы получаете и возвращаете область памяти для массива с типом, образом и именем (и возможно и другими атрибутами), которая специфицирована заранее с дополнительным атрибутом ALLOCATABLE. REAL, DIMENSION(:), ALLOCATABLE :: x : Allocate(x(N:M)) ! N и M целые выражения. : x(j) = q ! Некое присваивание массива. CALL sub(x) ! Использование массива в подпрограмме. : DEALLOCATE (x)
Освобождение происходит автоматически (если атрибут SAVE не был задан), когда достигается RETURN или END в этой программной единице. Третий вариант - это автоматический массив, это почти такой же как в старом Fortran, где x в примере ниже может быть в списке аргументов. Этого больше не требуется. (см. ниже). SUBROUTINE sub (i, j, k) REAL, DIMENSION (i, j, k) :: x Размеры для x задаются целыми величинами в вызове программы. Наконец "массив с неявным образом", где память определена в вызове процедуры и для которой даны только тип, ранг и имя. SUBROUTINE sub(a) REAL, DIMENSION (:,:,:) :: a Заметим, что в одном из примеров компилятор на Alfa не передал образ в подпрограмму. з Неявный массив образов (форм) Формальные массивы могут взять форму своего образа (своей формы) из соответствующих фактических параметров (см. замечание выше). з Автоматические массивы Массивы в процедуре имеют размеры, определяемые значениями переменных.
! Пример использования автоматических и неявных образов массивов program main integer :: dim=8 real :: a(4)=(/44.3,77.5,88.21,14.35/) call sub1(a,dim) end program subroutine sub1(x,dim1)
integer dim1 real x(:), b(dim1) ! Объявляет 'x' с тем же самым образом как у фактического аргумента 'a' . . ! Объявляет 'b' с 'dim1' элементами. . end subroutine sub1 з Секция (вырезка) массива Вы можете указать часть массива. Допустим, массив A специфицирован следующим образом REAL, DIMENSION(-4:0, 7) :: A Тогда A(-3, :) указывает второй ряд матрицы, тогда как A(0:-4:-2, 1:7:2) указывает (в обратном порядке) каждый второй элемент в каждой второй колонке. Так же как переменные могут образовывать массивы, так и константы могут образовывать массивы . Вырезки позволяют пользователю получить большее управление массивом путем использования тройки индексов и векторных индексов.. з Конструкция WHERE Конструкция позволяет задавать условия выполнения действий над массивами. В следующем примере мы хотим защититься от отрицательных элементов X. Это задано следующей конструкцией WHERE ( x = 0.0 ) z = 4.0*y*sgrt(x) ELSEWHERE z = 0.0 END WHERE Заметьте, что ELSEWHERE записано одним словом! Сравните с функцией SUM (конец следующей секции.
Сумма положительных значений членов массива записана и так
SUM ( X, MASK = X .GT. 0.0) Такой оператор не может быть использован для того, чтобы избежать деления на ноль в случае суммирования 1/X, что есть слово-маска для определения, какие номера включены в суммирование, и либо ни одно из двух определенное значение не может быть вычислено или нет. Но в последнем случае Вы можете использовать конструкцию WHERE. Присваивание маскированного массива позволяет пользователю сделать присваивание , базирующееся на маске по всему массиву.
! Пример использования распределяемого массива и конструкции where. program main integer number_of_students, number_of_tests ! Объявление массива 'scores' и 'letter_grade' как распределяемых allocatable и 2 мерные. integer, allocatable, dimension(:,:) :: scores character(len=1), allocatable, dimension(:,:) :: letter_grade open(unit=1,file='grades',form='formatted',access='sequential') read(1,10) number_of_students, number_of_tests ! Распределение памяти для массивов. allocate (scores(number_of_students,number_of_tests)) allocate (letter_grade(number_of_students,number_of_tests)) read(1,10) ((scores(i,j),i=1,number_of_students), j=1,number_of_tests) close(1) ! Используется конструкция where для присваивания значений массиву 'letter_grade' , ! основанному на значениях массива 'scores'. where(scores >= 90) letter_grade = 'A' where(scores >= 80 .and. scores < 90) letter_grade = 'B' where(scores >= 70 .and. scores < 80) letter_grade = 'C'
where(scores >= 60 .and. scores < 70) letter_grade = 'D' where(scores < 60) letter_grade = 'F' write(*,*) letter_grade deallocate(scores,letter_grade) ! Освобождение памяти, использованной массивами. 10 format(i3,t5,i3) end program з Встроенные
В Fortran 90 включены 75 новых встроенных функций: В другом руководстве сказано, что Fortran 90 включает около 100 встроенных функций и очень много встроенных подпрограмм. Ряд функций позволяет иметь определенными атрибуты и допустимые параметры среды программирования, такие как наибольшее положительное число целое и с плавающей запятой, как доступ к системным часам. Включен генератор случайных чисел. Все встроенные функции и подпрограммы описываются в соответствующем разделе. Функция Inquiry Находит в программе во время выполнения характеристики массива или статус указателя. Находит в компиляторе характеристики типа данного. Функция Transfer Функция TRANSFER рассматривает сущность одного данного так, как если бы оно было типом другого данного. Она позволяет определенную физическую область передать другой области без конверсии типа. Array Intrinsics Различные новые функции уменьшения (преобразования) массива (например, SUM), перемножение векторов и матриц, конструирование массива (SPREAD), преобразование образа, сдвиг, транспонирование (TRANSPOSE), и указание значений максимума и минимума. Функция SPREAD обсуждается более полно в решении примера (11.1). см.Summary.
Границы массива Некоторые встроенные функции достаточны, чтобы определить фактические границы массива по измерениям. DO (i = LBOUND(a,1), UBOUND(a,1)) DO (j = LBOUND (a,2), UBOUND (a,2)) DO (k = LBOUND(a,3),UBOUND (a,3)) где LBOUND дает нижнюю границу размерности и UBOUND дает ее верхнюю границу. з Манипуляции над битовыми* Установка, очистка, тестирование и сдвиг битов в целых данных. Выполняют логические AND, OR, исключающее OR и дополнение. Тип данных "bit" не включен в стандарт, но имеются допустимые варианты битовых операций над целыми согласно более ранним military стандартом MIL-STD 1753. Дополнительно включены битовые, восьмеричные и шестнадцатиричные константы, так же возможность использовать их в операциях ввода/вывода с помощью трех новых спецификаций формата. В операторе DATA вы можете использовать присваивание. B'01010101010101010101010101010101' для битовых, O'01234567' для восьмеричные и Z'ABCDEF' для шестнадцатиричных чисел. з Строковые переменные Величины CHARACTER могут быть расширены до включения пустой строки a = '' и присваивание частично перекрывающихся строк теперь разрешено a(:5) = a(3:7)
Новая встроенная функция TRIM, удаляющая заключительные пробелы, является важным добавлением. Вы можете теперь делать выбирать между апострофом ' и двойной кавычкой " в качестве ограничителя строки. К числу тех же средств относится возможность использования двойной кавычки для представления апострофа внутри строки.,и использования апострофа для представления двойной кавычки внутри строки. -----------------------------------------------------------------------з Модули и процедуры
Модули это коллекции данных, определений типов определений процедур, которые дают большую защищенность и общее замену понятия COMMON. Модули занимают важное место в том, чтобы положить вещи для совместного использования. Этот новый вариант программной единицы может содержать данные, процедуры, определения производных типов и блоки интерфейса (и даже блоки common), все, что может быть включено в единицу scoping с посощью оператора USE. Модуль может также быть использован в скрытом коде процедуры от клиентов . Один и тот же модуль может быть использован во многих программах. з Интерфейсы В соответствии с предложениями Metcalf и Reid (1990, 1992) в Фортран 90 введено требование явного интерфейса. Он выглядит так INTERFACE SUBROUTINE SUB(A) REAL, DIMENSION (:,:,:) :: A END SUBROUTINE SUB END INTERFACE Если Вы забыли INTERFACE или если Вы имеете ошибочный интерфейс, то Вы обычно получаете "ошибку сегментации", это означает, что программная единица может быть отсутствует. Компилятор может быть использован для проверки результатов если характеристики процедур доступны в явном интерфейсе. Тело интерфейса, определенного пользователем, выглядит очень похоже на оператор определения функции и ассоциируется с определениями. Компилятор знает интерфейс с каждой определенной процедурой внутри программной единицы. С интерфейсом компилятор будет проверять характеристики результата функций, с которым ожидаются при вызове и проверке характеристик каждого аргумента с которыми ожидается процедура.
Программисты общих (родовых) процедур могут использовать общие процедуры (процедуры с одним и тем же имененм) с помощью использования интерфейсного блока, чтобы проверить информацию для определения, какую процедуру вызвать. Специальная процедура чтобы быть вызываемой, использующая вид, тип и ранг (категорию) аргументов.
! Пример использования общих процедур module procedure_def ! Опрелить общий интерфейс 'force' для специальных функций ! 'real_force' and 'double_force'. interface force ! Объявляет функции и подставляемые типы аргумента. function real_force(mass, accel) result(force) real force, mass, accel end function real_force function double_force(mass, accel) result(force) double precision force, mass, accel end function double_force end interface end module program main use procedure_def ! Ассоциирует procedure_def с main. real rmass, raccel, rforce double precision dmass, daccel, dforce data rmass/2401.0/, raccel/9.81245/ data dmass/2401.0d0/, daccel/9.81245d0/
! Вызывает функцию 'force'. Компилятор реализует первый ! в 'real_force', и второй в 'double_force'. rforce = force(rmass, raccel) dforce = force(dmass, daccel) write(*,'(1x,1p,e16.9,t25,d16.9)') rforce, dforce end program ! Фактические функции выполняемы, когда сделан вызов 'force'. function real_force(mass, accel) result(force) real force, mass, accel force = mass*accel end function real_force function double_force(mass, accel) result(force) double precision force, mass, accel force = mass*accel end function double_force з Внутренние процедуры Внутренние процедуры делают более легкой организацию кода, который использован только в одном месте и уменьшает число внешних подпрограмм, которые необходимо знать программисту. Программная единица и процедуры модуля могут содержать свои собственные внутренние подпрограммы и функции. Интерфейс этих внутренних процедур известен из содержащую программную единицу. з Рекурсия*
Рекурсия стандартизована в 90. Если процедура должна вызываться рекурсивно, даже косвенно, это должно быть объявлено ключевым словом RECURSIVE . Например,
RECURSIVE FUNCTION factorial (n) RESULT (fac) Должна иметь возвращаемый результат, когда задано имя величины со спецификацией RESULT. з Параметры необязательные и ключевые слова Подпрограммы могут вызываться с параметрами, сопровождаемыми ключевыми словами, можно использовать параметры по умолчанию SUBROUTINE solve (a, b, n) REAL, OPTIONAL, INTENT (IN) :: b Может быть вызвана с помощью оператора CALL solve (n = i, a = x) Где два аргумента заданы с ключевыми словами вместо позиционных и где один из трех имеет значение по умолчанию. Если SOLVE внешняя подпрограмма, то она требует использования блока INTERFACE в вызывающей программе. Как видно из приведенного, необязательные аргументы с ключевым словом добавляются свободно при вызове подпрограммы или функции. В FORTRAN 77, Вы были обязаны указывать каждый аргумент. В Fortran 90, аргументы могут быть необязательными и их порядок произволен. Имена подставляемых аргументов (аргументов с ключевыми словами) те, которые использованы в вызове процедуры для разрешения связей между фактическими и подставляемыми аргументами. Новые встроенные функции позволяют процедуре определить, какие аргументы представлены в списке фактических аргументов. з Ввод
В Фортране 90 оператор NAMELIST может использоваться среди спецификаций. В примере ниже list2 это имя списка, a и b переменные типа real, i переменная типа integer . NAMELIST /list2 / a, i, x : READ (unit, NML = list2) которым нужно передать исходные данные следующего вида, но все переменные не могут быть заданы и он могут быть заданы в любом порядке.
&list2 X = 4.3, A = 1.E20, I = -4 /
-----------------------------------------------------------------------з Другие средства
Стандартная форма исходного текста
Комметарии вводятся с помощью восклицательного знака. Множество операторов на одной строке разделяются с помощью точки с запятой ; з Свободная форма исходного текста
Колонки не имеют никакого особого значения, важны пробелы, продолжение специальный символ в конце строки, комментарии вводятся в любой строке с помощью восклицательного знака. Множество операторов на одной строке разделяются с помощью точки с запятой ; . . Строка продолжения начинается с симоала & (амперсанд). Строки могут иметь более 132 символов, позволяются более длинные имена и более абзацные отступы для удобства чтения. з Длинные имена *
Максимальная длина 31 символ в обеих формах. Символы подчеркивания внутри имени разрешены. з Включение файлов *
Строка INCLUDE стандартизована. INCLUDE может быть использовано для включения из внешнего вида. Указывается полное имя файла, включаемого в фортран-программу. Рекурсия для оператора INCLUDE не разрешена. з Операции отношения *
Операции, подобные .LT. и .GT. могут быть заменены их математическими символами < и >, а также. < <= ==
.LT. .LE. .EQ.
> >= /=
.GT. .GE. .NE.
Пробел - значимый символ
Пробел в свободной форме записи является значимым символом. Команды, подобные ENDIF и GOTO могут быть записаны как END IF или GO TO, но не EN DIF или GOT O. Чтобы допустить значимые пробелы в старой (фиксированной ) форме исходного кода не будет возможно, в этом случае допустимо написать END в следующем виде E
N
D
з
Комментарий в конце подпрограммы
Как во многих типах алголо-подобных языков слово END может быть составлено с именем подпрограммы или функции, например, END FUNCTION GAMMA. з Fortran 90 в Lahey
Эта система объединяет проект победившего в конкурсе Fortran -компилятора с технологией фирмы Intel для построения высоко оптимизированного кода. Она оптимизирована для 386, 486, Pentium PC и полной реализации стандарта Fortran 90.
Более подробная информация
Если Вы заинтересованы в более глубоком и детальном знании Fortran 90, авторы рекомендуют переход на Fortran 90 Джеймса Кернигана (James Kerrigan). Вы можете купить эту книгу прямо у нас за $27.95 плюс $3.00 пересылка и вручение. REFERENCES Adams, Jeanne C., Walter S. Brainerd, Jeanne T. Martin, Brian T. Smith, and Jerrold L. Wagener. Fortran 90 Handbook. New York: McGraw-Hill, 1992. Baker, Steven. "Modernizing Fortran." Software Development November 1993, International Standards Organization, ISO/IEC 1539: 1991 (E). The International Fortran Standard. Kerrigan, James F. Migrating to Fortran 90. Sebastopol: O'Reilly & Associates, 1993. Metcalf, Michael and John Reid. Fortran 90 Explained. New York: Oxford University Press, 1990. *These features are already contained in Lahey's FORTRAN 77 language sytems, F77L and F77L-EM/32 . Описание Фортрана 90 разбито на разделы: О компиляции с (compilat_rus.html)
языка
Фортран
90 Вложенность конструкций Структура программы
Соглашения о кодировании Порядок операторов Объекты данных Синтаксис программы Main Переменные Формат размещения программы Спецификации или объявления Программные единицы Виды KIND Процедуры Арифметические операторы Родовые единицы Комментарии Модули Символьный тип Ввод-вывод Производные типы данных
Операции отношения
Интерактивный ввод-вывод
Логические выражения
Простой ввод-вывод
Сравнение символов
Форматированный ввод-вывод
Массивы (термины)
Редактирование ввода-вывода
Спецификации массива
Оператор Namelist
Вырезки (Сечения) массива
Списки ввода-вывода
Векторные указатели
Ввод-вывод без продвижения
Память для массивов
Файловый ввод и вывод
Присваивание массивов
Номер устройства
Массивы нулевого размера
Операторы READ and WRITE
Массивы переменных типов
Оператор OPEN
Инициализация массивов
Оператор CLOSE
Оператор WHERE
Оператор INQUIRE
Встроенные функции для массивов
Память для массивов
Условные операторы
Динамические массивы
Операторы управления
Распределяемые массивы
Циклы
Указатели для массивов Еще примеры
Компиляция и выполнение Компиляция После как программа на Fortran 90 спроектирована и ее исходный текст записан в файл (обычно с суффиксом .f90, а для Альфа с расширением .f), то следующим шагом является компиляция, инициализируемая командой f90 <опции> программы)
filename.o
filename.f
<библиотеки> (для
создания
однопроцессорной
или vf90 <опции> filename.o filename.f <библиотеки> (для создания многопроцессорной программы) типичный запуск такой vf90 -O0 -o $1.o $1.f -lm Каждая из этих команд вызывает компилятор, программу перевода исходного кода в промежуточный ассемблерный код и далее в машинный (объектный) код. Компилятор проверяет синтаксис операторов в соответствии со стандартом и семантикой операторов. Этот шаг генерирует версию объектного кода, которая сохраняется в другом файле, обычно с тем же именем, но с другим расширением (обычно .o в системе UNIX). Связывание (редактирование связей ) Редактор связей инициализируется в системе Альфа командой fld <опции> filename filename.o Простой вариант такой fld -o filename filename.o Он выполняет включение некоторого кода, который требуется, из библиотек или других предваритеьно прокомпилиованных файлов. Он генерирует версию выполняемого кода, которая снова запоминается в файле, расширение которого не создается (в системе UNIX по умолчанию это имя a.out) Для трансляции и загрузки можно создать bat. файл, например, такой vf90b.bat vf90 -O0 -o $1.o $1.f -lm fld -o $1 $1.o Наличие такого файла вызова позволяет запускать компиляцию и редактирование связей командой vf90b.bat filename Выполнение В однопроцессорном варианте инициализируется путем ввода имени выполняемого файла, что приводит к запуску программы. Во время выполнения программа может потерпеть аварию, если происходят ошибки выполнения Как и в при использовании других компиляторов, логические ошибки не могут
быть проверены компилятором и они зависят от программиста, находящего и удаляющего их. Единственный путь состоит в новых и новых тестированиях с данными, для которых известен результат. Нужно рассмотреть все возможные варианты исходных данных. Предварительная забота об этом должна быть осуществлена во время начального проектирования программы. Идентификация ошибок на фазе проектирования дешевле, чем на более поздних стадиях. Для многопроцессорной задачи перед ее запуском должен быть создан паспорт с помощью команды madd. В паспорте задается имя файла, созданного загрузчиком, имя программы в program (его следует заканчивать символом "подчеркивание"), имена каталогов, где размещается абсолютный модуль, имя каталога, где размещаются файлы ввода-вывода. А также запрашиваются сведения о предполагаемом времени решения, числе требуемых процессоров и числе перезапусков задачи по аварии. Все эти сведения вводятся в ответ на запросы системы, которые появляются после ввода команды. madd <имя задачи> Затем задача запускается командой run <имя задачи>
Примеры Приведем примеры запусков компилятора f90 на Альфа Команда f90 ax.f приводит к компиляции файла ax.f, созданию выполняемого файла a.out. Оптимизацию компилятор проводит по умролчанию по умолчанию (см. опиции компилятора).
Команда f90 -o abc ax.f bx.f cx.f использует флаг -o для именования выполняемого файла именем abc и компилирует файлы ax.f90, bx.f90, и cx.f90 как одну программу. Межпроцедурная оптимизация распространяется на ax, bx, и cx. Создается объектный файл abc.o и выполняемый файл *abc.
Команда
f90 -c ax.f bx.f cx.f использует флаг -c для поддержания связи и изготовления индивидуальных объектных файлов ax.o, bx.o, и cx.o. Межпроцедурная оптимизация не проводится. Команда f90 -c -o abc.o ax.f bx.f cx.f использует флаг -c для поддержания связи и флаг -o для создания единственного объектного файла abc.o. Межпроцедурная оптимизация проводится. Сведения о трансляции с помощью vf90 и запуске многопроцессорных программ смотри в соответствующем разделе.
Соглашения о записи кода В этих заметках все примеры кода написаны в стиле 'typewriter' , то есть PROGRAM hi ! вывод сообщения WRITE(*,*) 'Hello World!' END PROGRAM hi Соглашения о записи кода состоят в том, что • • • • •
Все ключевые слова и имена встроенных процедур (эти команды и функции есть часть стандарта) набираются на верхнем регистре, все остальное на нижнем Чтобы помочь чтению кода тела модуля программы, с отступом печатаются блоки INTERFACE, блоки DO , блоки IF , блоки CASE и т.д. Имя программы, подпрограммы или функции включается в оператор конца END В операторе USE фраза ONLY использована для явного документирования всех объектов, которые доступны из этого модуля В операторе CALL и вызове функции ключевое слово аргумента всегда используется для необязательных аргументов.
Объекты данных Говоря об объектах данных, будем иметь ввиду •Встроенные типы •Литеральные константы •Неявноые типы •Опреления численных и логических
•Определение символьных •Константы (Параметры) •Инициализация Переменные
В разделе объясняется смысл понятия "переменная" и правила задания идентификаторов: составляются из букв, цифр, возможно подчеркивание, большие и малые буквы считаются эквивалентными. Переменные величины хранятся в памяти, идентификатор дает название тому месту в памяти, где значения хранятся, с переменной соотносится значение (число, текст, другие значения), способ представления значений в памяти и размер памяти, требуемый для размещения значения, зависит от типа переменной, тип определяется специальным оператором или по умолчанию (см. далее). В примерах текстов будут использоваться большие буквы для зарезервированных слов и идентификаторов, малые - для объектов пользователя. В качестве примера можно привести следующие значения переменных 7 96.4 3.14159 с именами переменных: daysinweek temperature pi Тип переменных задается спецификацией типа. Имеется несколько форм численных данных, а именно:
Целые Могут иметь только дискретные значения (такие как. -3124, -960, 10, 365, и т.п..)
Вещественные Могут иметь дробную часть (такие как 10.3, -8.45, 0.00002, и т.п..) и обычно имеют больший диапазон возможных значений, чем целые Комплексные числа Имеют вещественную и мнимую части (например, 3-2i, -5+4i, и т.п.).
Целые более точны для дискетных значений и быстрее обрабатываются, но вещественные нужнее для многих вычислений. Комплексные нужны для некоторых научных приложений. Иногда программы на фортране требуют и других типов данных. Отдельные буквы, слова и фразы могут быть представлены символьным типом, тогда как логические значения `true' и `false' представляются логическим типом. Наконец, необходима возможность явно указывать значения величин, которые называют литеральными константами, например 3.14159. Соглашения об именах В программах на Фортране имена переменных должны соответствовать соглашению об именовании. Это соглашение разрешает длину имени от 1 до 31 алфавитно-цифровых символа - сформированных из 26 доступных букв: a...z; и 10 цифр 0...9; и символа подчеркивания _ - при этом первым символом должна быть буква. Заметим, что бльшие и малые буквы считаются неразличимыми и считаются эквивалентными. Таким образом имена name, Name, NaMe и NAME все ссылаются на один и тот же объект. В отличие от некоторых языков программирования, в которых определенные слова зарезервированы и могут использоваться программистом только в определенном смысле и контексте, Fortran не имеет таких ограничений. Тем не менее программист должен следить, когда при именовании переменных делается попытка использовать любые слова, входящие в язык. Правильные имена переменных: x, x1, mass, cost, day_of_the_week
Правильные имена переменных (но не используйте!): real, integer, do, subroutine, program
Неправильные имена переменных : ten.green.bottles, 1x, a thing, two-times, _time
Спецификации или объявления Все переменные, использованные в программе, должны иметь ассоциированные с ними типы данных, такие как REAL, INTEGER или COMPLEX, которые обычно идентифицированы в начале прогаммы. Они называются объявлениями или спецификациями переменных, например: REAL :: temperature, pressure INTEGER :: count, hours, minutes Объявляет 5 переменных, две их которых имеют значения, являющиеся вещественными числами, и три имеют целые значения. Оператор объявления переменных может быть использован для присваивания начальных значений объявленным переменным. Если начальное значение не задано переменной, она не может быть принимать никаких значений до тех пор, пока не будет присвоено с помощью оператора присваивания. REAL :: temperature=96.4 INTEGER :: days=365, months=12, weeks=52 В общей форме объявление переменной: тип [,атрибуты...] :: список переменных где тип один из встроенных типов данных: INTEGER REAL COMPLEX CHARACTER LOGICAL
А атрибут ... необязательный список "ключевых слов", каждое отделяется запятой, ключевые слова используются для определения свойств переменных:
ALLOCATABLE INTENT(...) PARAMETER PUBLIC DIMENSION(...) INTRINSIC POINTER SAVE EXTERNAL OPTIONAL PRIVATE TARGET
CHARACTER и LOGICAL типы данных, обсуждаемые в отдельном разделе. Атрибуты введены как требуемые к ним заметки. Параметры (Parameters) Термин параметр в Фортране вводит в некоторое заблуждение. Он относится к значению, которое будет заменено во время выполнения программы. Например, программист может хотеть значение pi сделать неизменяемым программой. Тогда pi может объявлено: REAL, PARAMETER :: pi=3.141592
REAL специфицирует тип данного; атрибут PARAMETER в дальнейшем определяет переменную pi. Всем параметрам должны быть даны значения в операторе их объявления (в нашем случае 3.141592). Параметры могут быть определены для других типов данных, например:
INTEGER, PARAMETER :: maxvalue=1024 INTEGER, PARAMETER :: repeatcount=1000 Считается ошибкой попытка переопределения значения параметра в ходе выполнения программы.
Неявные объявления Фортран 90 разрешает тип переменных real и integer объявлять неявно без соответсвующего оператора объявления. Это свойство неявного объявления обеспечивает преемственность с более ранними определениями языка Фортран, но может создавать проблему
программирования при неосторожности. Можно отключить это свойство с помощью оператора: IMPLICIT NONE В начале каждой программы. Это заставит программиста объявлять все переменные, которые использованы в программе, чтобы не приводить к ошибкам во время компиляции. Явное объявление типов позволяет вместе с определениями типов задавать начальные значения. При неявном описании начальная буква имени определяет тип целый для имен, начинающихся на буквы I, J, K, L, M и N ; и тип вещественный для имен, начинающихся на буквы от A до H и от O до Z. KIND
Каждый тип данных имеет одно или более значений параметра типа KIND, связанного с ним. Типы данных с различными значениями KIND используют различное число байтов для запоминания информации. Это означает, что числовые типы данных с различными параметрами типом KIND имеют различный диапазон допустимых значений и/или различного уровня числовой точности. Например, компилятор NAG F90 использован для развития этого курса, обеспечившего три значения параметра типа KIND для типа INTEGER (KIND=1, 2 или 3); 3 являлось по умолчанию. Другие компиляторы или компилятор NAG F90 в различных системах могут поддерживать различные значения KIND. Переменные объявлены с желаемой точностью с помощью использования атрибута KIND: тип (KIND = kind_type_value) [, атрибуты...] :: список переменных Например: INTEGER :: a ! по умолчанию KIND=3 INTEGER(KIND=3) :: b ! по умолчанию INTEGER(KIND=1) :: c ! ограниченная точность -127 <= c <= 127 INTEGER(2) :: d ! KIND= необязательное INTEGER :: e=1_2 ! e=1 и тип kind 2
Оба типа данных INTEGER, и LOGICAL имеют некоторые возможные значения типа KIND, каждый из которых использует меньше памяти, чем по умолчанию (которая в случае типа INTEGER приводит к меньшему диапазону возможных значений - так что остерегайтесь!).
Эти альтернативные значения KIND обычно использованы только, когда память данных выше номинала.Для типа CHARACTER обычне имеется одно значение KIND . Типы REAL (и COMPLEX) имеют два значения KIND. По умолчанию (KIND=1) дается более низкий уровень точности, чем (KIND=2). (Аналогично этим значениям KIND в Fortran 77 имеется одиночная и двойная точности переменных.) Обычно можно использовать REAL с KIND=2, чтобы хранить более высокую точность и/или когда предполагается большой диапазон значений , как показано ниже: REAL :: a ! по умолчанию KIND=1 REAL(KIND=2) :: b, c ! большой диапазон и / или точность COMPLEX(KIND=2) :: d ! большой диапазон и / или точность
Точный уровень точности значений может быть проверен с помощью встроенных функций. Встроенные функции RANGE(), HUGE(), PRECISION(), и др. Все дают информацию о пределах (ограничениях) KIND для переменных различных типов. Ниже примеры из компилятора NAG F90: INTEGER :: a ! по умолчанию KIND=3 INTEGER(KIND=2) :: b REAL :: c ! по умолчанию KIND=1 REAL(KIND=2) :: d ! большой диапазон и / или точность HUGE( b ) ! наибольшее число = 32767 HUGE( c ) ! наибольшее число = 3.4028235E+38 HUGE( d ) ! наибольшее число = 1.7976931348623157*10**308 RANGE( a ) ! наибольшая степень = 9 RANGE( d ) ! наибольшая степень = 307 PRECISION( c ) ! точность (в цифрах) = 6 PRECISION( d ) ! точность (в цифрах) = 15
Переносимость
Количество и значение параметров всех KIND зависит от используемого компилятора. Вы поймете, что явно используя значение (такое как в (KIND=3) ), программа может не работать (или давать неожиданно ошибки), когда используются разные компиляторы. Например, некоторые компиляторы используют KIND=1,2 и 3 для типа INTEGER , тог как другие используют KIND=1,2 и 4. Один способ редактирования программы для различных значений KIND это использование встроенных функций SELECTED_REAL_KIND() и SELECTED_INTEGER_KIND(). Для целых, SELECTED_INTEGER_KIND() делает управляемым сохранение данных с максимальной степенью данные (то есть 10r, где r есть диапазон) и функция возвращает параметр , ассоциированный с типом KIND. Для вещественных, SELECTED_REAL_KIND() делает выбираемым оба диапазона и десятичную точность и снова возвращает значение , ассоциированное с KIND .
INTEGER, PARAMETER :: k2 = SELECTED_REAL_KIND(10,200) REAL(KIND=k) :: a ! диапазон=+/-10**200, 10 десятичных позиций INTEGER, PARAMETER :: k5 = SELECTED_INTEGER_KIND(5) INTEGER(KIND=k5) :: b ! диапазон=+/-10**5
Если компилятор не может поддержать запрашиваемый диапазон и/или уровень точности, функция SELECTED_type_KIND() возвращает отрицательное значение программа не будет компилироваться.
Преобразование типа
Когда присваивается одна переменная другой (или переменные одного типа, но с разными видами KIND ), нужно обеспечить, чтобы данные остались совместимыми. Когда присваиваемые данные различных типов (наприме, присваиваются вещественные целым) или когда присваиваемые данные различных типов kind (например, 4-байтное INTEGER присваивается однобайтовому INTEGER), где информация теряет смысл. Существует ряд встроенных функций, которые регулируют преобразование данных в достоверном и совместимом виде. Например,
REAL :: total=13.0, num=5.0
INTEGER :: share share = total/num ! total/num=2.6, share=2 share = INT( total/num ) ! total/num=2.6, share=2 Результат total/num это вещественное число, поэтому он в примере REAL нужно присвоить целому INTEGER. Заметим, что значение, присвоенное величине share, будет усечено (nне округлено к ближайшему!). Встроенная функция INT() конвертирует ее аргумент (то есть результат для total/num) в целое, и может быть присвоена текущему, как только численные нецелые присвоены переменной типа INTEGER. Другие типы данных имеют функции преобразования простого типа; REAL() преобразует ее аргумент к типу REAL и CMPLX()преобразует ее аргумент к типу COMPLEX (часто усекая мнимую часть). Возможно преобразовать данные CHARACTER в INTEGER (и наоборот) используя встроенные функции IACHAR() и ACHAR(). Чтобы разрешить преобразование различных типов KIND, каждая из функций преобразования должна быть снабжена значениями типа KIND , которые могут быть типами для конвертируемыми данными. Например:
INTEGER, PARAMETER :: k2=SELECTED_INT_KIND(2) INTEGER :: long ! по умолчанию kind=3 INTEGER(KIND=K2) :: short REAL(KIND=2) :: large long = 99 short = INT( long, KIND=k2 ) ! конвертирует 99 в INTEGER(KIND=k2) large = REAL( short, KIND=2 ) ! конвертирует 99 в REAL(KIND=2)
Остерегайтесь! Когда преобразуются данные из одного типа в другой, переменная, принимающая данные, должна быть способна хранить значение (диапазон и точность). Если не обеспечена, возникает ошибка:
INTEGER(KIND=1) :: short ! -127 <= short <= 127 INTEGER :: long=130 ! -32767 <= long <= 32767
short = long ! ошибка short = INT( long, KIND=1 ) ! еще ошибка Арифметические выражения
Числовые переменные, параметры и литеральные константы могут быть скомбинированы с использовавнием знаков + (сложение), - (вычитание), * (умножение), ./ (деление) и ** возведение в степень, и результат присваивается переменной с использованием операции =. Напрмер: estimate_cost = cost * number actual_cost = cost * number + postage sum = 10 + 3 circumference = 2 * pi * radius
Арифметические выражения могут включать скобки, которые могут быть использованы для обеспечения требуемой последовательности операций в выражении. Например: y = 1+x/2
можно интерпретировать как "прибавление 1 к х и последующее деление результата на 2" или как "прибавление 1 к половине х ". Использование скобок может сделать эти варианты более понятными: y = 1+(x/2) y = (1+x)/2
Любое выражение, которое появляется внутри скобок, должно вычисляться первым. В выражении, которое содержит более одной операции , выполняется следующее (операции выполняются слева направо с учетом старшинства операци. А именно: •выражения в скобках, (...). •возведение в степень, **.
•умножение или деление, * или /. •сложение и/или вычитание, + или -.
Операции с одинаковым старшинством выполняются в порядке слева направо area = pi*radius**2 ! pi*radius*radius area_not = (pi*radius)**2 ! pi*radius * pi*radius Комментарии
Все программы могут иметь текстовый комментарий , объясняющий структуру и смысл каждого раздела программы. Все символы, появляющиеся в строке справа от символа !, игнорируются компилятором и не. Текст, появляющийся в после символа ! , называется комментарием и он будет использован читателем программы , который эту программу будет пататься изменять или использовать. Это важно в частности, если программа будет переделываться в будущем. area = pi*radius*radius !Вычисление площади круга Комментарий использован для представления действия оперетора.. Следующий оператор содержит только комментарий и не выполняется. ! WRITE (6,*) temp, radius*radius Тип Character
В предыдущих разделах были введены встроенные численные типы REAL и INTEGER. Третий встроенный тип CHARACTER представлен в данном разделе. Этот тип используется для манипуляции с данными, представляющими слова и предложения. В языке Fortran строки имеют длину, индивидуальные символы могут вызываться по позиции (самый левый символ занимает позицию № 1). Как и с численными типами, программист может специфицировать литеральные константы встроенного типа character как описано ниже.
Константы Character
Пример ниже взят из программы, которая вычисляет площадь окружности. Программа читает значение радиуса, выводит площадь окружности. Без приглашения для пользователя вид такой программы очень неудобен - так как не обозначено, что именно нужно вводить. Включение символьных констант (или литералов) в вывод делает программу для пользователя более понятной. Например: WRITE (*,*) `Пожалуйста, напечатайте значение радиуса окружности' READ (*,*) radius area = pi*radius*radius WRITE (*,*) `Площадь окружности с радиусом `, radius, & ` равна `, area Символы, которые появляются между двумя апострофами, это символы константы и они появятся на экране:
Пожалуйста, напечатайте значение радиуса окружности' 12.0 Площадь окружности с радиусом 12.0 равна 452.38925
Символы двойной кавычки могут быть использованы для определения символьных литералов. Если строка символов содержит символы ограничителей (апострофы или двойные кавычки), то другое может быть использовано в качестве ограничителей. Тем не менее, если строка содержит оба символа ограничителя или программист хочет всегда определиять строку, используя тот же символ, что и ограничитель, то ограничитель может быть задан в строке как два близких ограничителя (апострофы или двойные кавычки, как подойдет). Эти, что показаны как одиночный символ.
"Эта строка содержит апостроф '." ' Эта строка содержит двойные кавычки ".' " Эта строка содержит апостроф ' и двойные кавычки ""."
Это могло бы появиться в выводе как:
Эта строка содержит апостроф '. Эта строка содержит двойные кавычки ". Эта строка содержит апостроф ' и двойные кавычки ".
Символьные переменные
Переменные REAL и INTEGER. Следующий оператор объявляет две символьные перменные, каждая из которых содержит один символ: CHARACTER :: yesorno, sex
Значение, присваиваемое символьной переменной в форме символьной константы, таким образом: yesorno = 'N' sex = 'F'
Символьные переменные, содержащие несколько символов, известны как строки. Например, чтобы запомнить персональное имя, можно сделать следующее определение и присваивание: CHARACTER(LEN=12) :: surname, firstname CHARACTER(LEN=6) :: initials, title title = 'Prof.' initials = 'fjs' firstname = 'Fred' surname = 'Bloggs'
Заметим, что все определены с указанием длины, чтобы присваивать литеральные константы. Переменные, которые имеют непривычные символы, заполнены пробелами до конца. Если переменная не достаточной длины, чтобы содержать символы, присваиваемые ей, то могут быть использованы самые левые, а избыток отсечен. Например: title = 'Professor'
будет эквивалентно оператору: title = 'Profes'
Общая форма объявленияя символьной переменной такова:
CHARACTER [(LEN= )] [,attributes] :: name
Манипуляции с символами
Конкатенация
Арифметические операции, такие как + и - , могут быть использованы и с символьными переменными. Только для символьных определена операция конкатенации со знаком //. Она может быть применена для объединения двух строк:
CHARACTER (len=24) :: name CHARACTER (len=6) :: surname surname = 'Bloggs' name = 'Prof ' // ' Fred ' // surname
Как с символьными литералами, если выражение, использующее операцию //, превышает длину переменной, то лишние символы справа отсекаются, и если слишком мало символов специфицировано, то символьные позиции справа заполняются пробелами.
Подстроки
Подстрока это часть большой символьной строки. Символы в строке могут быть указаны с помощью позиции в строке, начиная от первого самого левого символа.
CHARACTER (LEN=7) :: lang lang = 'Fortran' WRITE (6,*) lang(1:1), lang(2:2), lang(3:4), lang(5:7)
Приведет к следующему выводу на экран:
Fortran
Подстрока специфицирована с помощью начальной и конечной позиции (start-position : endposition). Если значение для начальной позиции start-position опущено, берется 1 и если значение конечной позиции end-position опущено, берется максимальная длина строки. Так, lang(:3) эквивалентно lang(1:3) и lang(5:) эквивалентно lang(5:7).
Значения start-position и end-position должны быть целыми или выражениями, имеющими целое значение. Значение start-position должно быть >= 1 , значение end-position <= длина строки. Если start-position > максимальной длины или end-position , то в результате получится строка нулевой длины.
Встроенные функции
О функциях пойдет речь более глубоко позднее в курсе, как бы ни было это полезно ввести на этой ранней стадии. Встроенные функции представляют действия, которые определены в стандарте языка и функции, табулированные ниже, относятся к символьным строкам. Эти встроенные функции представляют несколько общих, требующих символьных манипуляций:
•LEN(string) возвращает длину символьной строки •INDEX(string,sustring) находит место подстроки в другой строке, возвращает 0, если не находит •CHAR(int) преобразует целое в символ •ICHAR(c) преобразует символ в целое •TRIM(string) возвращает строку с завершающими пробелами, перемещенными.
Преобразование символьных и целых основаны на том, что доступнные символы представляют собой последовательность, а целые значения представляют позицию в последовательности набора символов. Поскльку они являются некоторой позицией в последовательности набора символов и к тому же машинно-зависимы, то соответсвующие целые значения здесь не обсуждаются. Тем не менее возможно установить, чтобы независимо от фактическую последовательность, имело место следующее:
INTEGER :: i CHARACTER :: ch ... i=ICHAR(CHAR(i)) ch=CHAR(ICHAR(ch))
Ниже пример того, как могут быть использованы встроенные функции:
CHARACTER(len=12) :: surname, firstname INTEGER :: length, pos
... length = LEN(surname) ! длина=12 firstname = 'Walter' pos = INDEX(firstname, 'al') ! pos=2 firstname = 'Fred' pos = INDEX(firstname, 'al') ! pos=0 length = LEN(TRIM(firstname)) ! длина=4 Производные типы данных
Определение и спецификация Во многих алгоритмах имеются объекты данных, которые могут быть сгруппированы вместе , чтобы с формировать структуру. Они могут быть нужны по соображениям обеспечения читаемости, удобства или надежного программирования. Например, объект "окружность" можно рассматривать с такими свойствами как: radius area Программист может определить специальные типы данных, известные как производные типы данных , чтобы создать составные структуры. Окружность может быть смоделирована следующим образом: TYPE circle INTEGER :: radius REAL :: area ENDTYPE circle
Создадим шаблон, который будет использоваться для объявления переменных этого типа: TYPE(circle) :: cir_a, cir_b
Производные типы могут быть сконструированы из произвольного числа или комбинации встроенных типов данных (или из других ранее определенных производных типов данных). Общий вид оператора TYPE таков: TYPE :: имя Операторы, определяющие компоненты .. ENDTYPE [имя]
TYPE(имя) [,атрибуты] :: список переменных
Заметим, что имя не обязательно в операторе ENDTYPE, но полезно для обеспечения читаемости программы. Подобно встроенным типам данных компонентам производных типов могут быть даны начальные значения. Например: TYPE (circle) :: cir=circle(2,12.57)
Производный тип может быть назван так же как встроенный, такой как REAL и INTEGER. Не смотря на то, что они могут быть использованы в определениях других типов. Например, point определен так: TYPE point REAL :: x, y ENDTYPE point Тогда ранее определенный тип circle можно модифицировать, включив позицию центра окружности в определение:
TYPE circle TYPE (point) :: centre INTEGER :: radius
REAL :: area ENDTYPE circle
Включение одного оператора внутрь другого блока операторов называется вложением .
Обращение к компонентам Индивидуальные компоненты производного типа могут быть доступны через использование имени переменной и имени компоненты, отделенной символом %, например: cir_a%radius = 10.0 cir_a%area = pi * cir_a%radius**2
Если производный тип имеет компоненту, которая является производным типом, то компонента может быть доступна следующим образом: cir_a%position%x = 5.0 cir_a%position%y = 6.0
Компоненты производного типа могут появиться в любых выражениях и операторах, которым эти типы данных разрешены. Возможно присвоить один экземпляр производного типа другому экземпляру того же производного типа. Операция = есть только операция, которую можно применить к переменной производного типа., все другие операции требуют от программиста явного доступа к компонентам покомпонентно. cir_a%radius = cir_b%radius cir_a%area = cir_b%area cir_a%position%x = cir_b%position%x cir_a%position%y = cir_b%position%y cir_a = cir_b ! коротко для всего выше cir_a = cir_b * 2 ! неверно
Операции отношения Логические переменные, определенные словом LOGICAL, могут принимать одно из двух значений (.TRUE. or .FALSE.). Обращение к логическим перменным имеет вид: LOGICAL [, атрибуты] :: переменная Логической переменной может быть присвоено явно значение или с помощью выражения, например: LOGICAL :: guess, date LOGICAL, PARAMETER :: no = .false. INTEGER :: today_date ... guess = .true. date = (today_date==5) если today_date предварительно присвоено значение и оно равно 5, то date равно .TRUE., в противном случае .FALSE.. Операция отношения == называется "равно", тогда today_date==5 читается как ' today_date равно 5?'. Ниже приведен список операций отношения: • • • • • •
< меньше чем <= меньше чем или равно больше чем >= больше чем или равно == равно /= не равно
Ниже приведены примеры использования операций отношения: LOGICAL :: test INTEGER :: age, my_age CHARACTER(LEN=5) :: name ... test = 5 < 6 ! True test = 5 > 6 ! False test = 5 == 6 ! False test = 5 /= 6 ! True test = 5 <= 6 ! True
test = 5 >= 6 ! False ... test = age > 34 ! переменная сравнивается с константой test = age /= my_age ! две переменные сравниваются test = 45 == my_age ! переменная может появляться в любой стороне test = name == 'Smith' ! символьные можно использовать test = (age*3) /= my_age ! допустимы выражения Логические выражения Выражения, содержащие логические переменные и/или операции отношения, могут быть объединенны в логическое выражение с использованием следующих операций: • • • • •
.AND. логическое пересечение .OR. логическое объединение .NOT. логическое отрицание .EQV. логическое тождество .NEQV. логическое отрицание тождества
Операция логического пересечения .AND., требует двух выражений или переменных, дает результат .TRUE. только в том случае, если оба выражения истинны, в противном случае дает значение .FALSE.. Рассмотрим следующий пример: LOGICAL :: test, employed=.true. INTEGER :: age=50 ... test = employed .AND. (age<45) ! test=.false. Здесь два подвыражения, один имеет значение .TRUE. , второй .FALSE. .Поэтому результат .FALSE. . Операция логического объединения .OR. требует двух выражений или переменных, дает результат .TRUE. в том случае, если хотя бы одно из выражений истинно, в противном случае дает значение .FALSE.. Рассмотрим следующий пример: LOGICAL :: test CHARACTER(LEN=10) :: name = 'James' ... test = (name='Dimitris') .OR. (name='James') ! test=.true.
Операция логического отрицани .NOT. используется для отрицания (инверсии) логического значения выражения, то есть .TRUE. дает .FALSE. и наоборот. Например: INTEGER :: big=100, small=2 LOGICAL :: test ... test = .NOT. (big>small) ! test=.false. test = .NOT. test ! test=.true. где оператор, заключенный (необязательные) получает значение, которое инвертируется. Операция логического тождества .EQV.чтобы проверить совпадение значений операндов (переменных или выражений) (оба могут иметь значения .TRUE. или .FALSE.). Если значения совпадают, результат .TRUE., в противном случае .FALSE..Например, в: LOGICAL :: test ... test = (5*3>12) .EQV. (6*2>8) ! test=.true. test = (5*3<12) .EQV. (6*2<8) ! test=.true. обоих операторах вычисляется .TRUE., поскольку подвыражения в обоих операторах имеют одинаковые значения. Операция отрицания логического тождества .NEQV. используется для получения значения .TRUE. только если операнды имеют различные логические значения, в противном случае значением станет .FALSE.. Например, в: LOGICAL :: test ... test = (5*3>12) .NEQV. (6*2>13) ! test=.true. test = (5*3>12) .NEQV. (6*2<13) ! test=.false. первое выражение имеет одну истинную компоненту и одну ложную, поэтому результат .TRUE.. Второе выражение имеет две истинные компоненты, поэтому результат .FALSE.. Когда сравниваются значение REAL со значением INTEGER компилятор будет конвертировать values the целое к типу REAL. Сравнение значения типа REAL с REAL должно выполняться с осторожностью; ошибки округления по точности переменных типа REAL могут означать, что два числа REAL никогда не будут равны, если их значения численно близки. Поэтому, рекомендуется тестировать их разницу, чем фактические значения: REAL :: a=100.0, b LOGICAL :: equal b = 2*50.0 equal = (a==b) ! потенциальная ошибка equal = (a-b)<0.0005 ! лучше программировать так Сравнение символов
Могут быть выполнены некие определенные правила, когда сравниваются символы или символьные строки. (A больше чем B ?). Более вадно, когда один из символьных строк короче другой, она дополняется пробелами (справа). Сравнение производится посимвольно. Сравнение начинается слева направо. Заканчивается оно тогда, когда обнаруживаются несовпадающие символы или достигается конец строки. Если в строках не найденв различия, они считаются одинаковыми, в противном случае сравнение заканчивается на первом несовпадающем символе. Сравнение символьных строк зависит от схемы упорядочения символов конкретной машины. Для языка схема упорядочения символов подчиняется следующим правилам: • • • • •
A < B < ... < Z 0 < 1 < 2 ... < 9 также пробел < A < B < ... < или пробел < 0 < 1 < 2 ... < 9 < A < B < ... < Z a < b < ... < z также пробел < a < b < ... < или пробел < 0 < 1 < 2 ... < 9 < a < b < ... < z
Z
<
0
<
1
<
2
...
<
9
z
<
0
<
1
<
2
...
<
9
Заметим, что стандарт для языка Fortran не позволяет определить, относительный порядок символов для нижнего и верхнего регистра символов. Символ, более ранний в схеме упорядочения символов, имеет меньшее значение. Пробел меньше, чем цифра, и меньше, чеи буква. Например: 'Alexis' > 'Alex' Правая строка в примере короче. Подстрока 'Alex' первого слова совпадает с подстрокой 'Alex ' второго (4 первые символа), а далее буква i первого слова больше чем пробел во втором слове. Поэтому результатом сравнения является значение .TRUE. Переносимость результата Схема упорядочения символов зависима от машины. Встроенные функции для сравнения символьных строк доступны, если базируются на универсальной кодировке ASCII: • • • •
LGT(string1, string2) ! больше чем или равно LGE(string1, string2) ! больше чем LLE(string1, string2) ! меньше чем или равно LLT(string1, string2) ! меньше чем
Так как схема упорядочения символов (кодировка) может быть различной для разных машин, то выше приведенные функции, которые базируются на универсальной кодировке ASCII, могут быть использованы для сранения строк. Доступны болшее число встроенных функций. Например, встроенные функции, которые определяют позицию символа в последовательности ASCII или в схеме упорядочения символов машины.в. Терминология по массивам
Массив и элемент
В предыдущем разделе введены понятия типов данных INTEGER, REAL и CHARACTER, и производные типы. В этом разделе вводится понятие об организации и структуризации данных, называемых массивом. Это коллекция данных, все элементы которой одного типа, элементы выстроены регулярным образом по одному образцу. Допустимы 3 типа массивов (в отношении к тому, как массив располагается в памяти): Статические массивы Наиболее общий тип и имеет свои фиксированные объявленный размер. Число элементов в массив не может быть изменено во время выполнения программы. Это неудобно в некоторых случаях (чтобы изменить размеры массива нужно отредактировать программу и перекомпилировать ее) и расточительно в терминах пространства памяти (иногда может быть задана большая память, а используется толко часть). Полудинамические массивы Имеют размер, определенный в подпрограмме. Массив создается так, чтобы подбирать требуемый размер, но только используемый в пределах подпрограммы. В Fortran 90 такие массивы - это собственная категория, такой массив называется автоматическим массивом. Динамические массивы Могут во время выполнения менять размер и используемый им объем памяти. Это дает свободу, но замедляет время выполнения и не позволяет контролировать границы во время трансляции. В Fortran 90 такие массивы называются распределяемыми (allocatable). Массивы используются, поскольку: • • • • • •
Легче описывать элементы (одно имя переменной вместо десятков или многих тысяч) Легче оперировать элементами Свободный доступ к элементам Легче понимать код Параллелизм, присущий данным, реализуется в понятии массива Возможность оптимизации удобных случаев (для проектирования компиляторов)
Каждый элемент имеет указатель (индекс) для идентификации его места в массиве. Принято, что нумерация начинается с 1.: Свойства массива
Ранг (или размерность) массива определяется числом индексов, требуемых для указания элемента массива. Скалярная переменная имеет ранг 0, массив с рангом 1 называют вектором, массив с рангом 2 - матрицей. Рассмотрим снова следующие массивы:
5
7
13
24
5
7
13
24
0
65
5
22
0
65
5
22
Верхний массив - вектор или одно-мерный массив. Матрица - это двумерный массив. Наибольшая размерность 7. Границы определяют наименьший и наибольший индекс в каждом измерении. Протяженностью (extent) массива называют число элементов в измерении. Образ (shape) массива - это вектор, вмещающий протяженности массива. Термин размер относится к числу элементов в массиве, это произведение протяженностей. Массивы, имеющие одни и те же образы, называют согласованными, соответствующими (conform). Согласованность является условием для операций над целыми массивами или сечениями (сегментами, вырезками) массивов. В вышеприведенном массиве имеются такие массивы. Все массивы совместимы со скалярными значениями (All arrays conform with scalar values). Спецификация массивов
Подобно переменным массивы специфицируются типом данных (INTEGER, REAL, derived type, и т.д.). Для статических массивов ранг (не более 7) и границы (верхняя и нижняя) задаются по каждому измерению. Объявление нижней границы не обязательно. Если нижняя граница не задана, то Fortran 90 полагает, что нижнее значение индекса равно 1. Альтернативными и эквивалентными формами можно объявлять массив следующим образом
тип, DIMENSION(граница) [, атрибут] :: имя тип [,атрибут ] :: имя (граница) где атрибут учитывается декларациями других атрибутов, если требуется.
Следующие декларации эквивалентны. Обе объявляют целый массив а с шестью элементами, вещественный массив b с десятью элементами и двумерный логический массив yes_no. Первое объявление: INTEGER, DIMENSION(6) :: a REAL, DIMENSION(0:9) :: b LOGICAL, DIMENSION(2,2) :: yes_no Второе объявление: INTEGER :: a(6) REAL :: b(0:9) LOGICAL :: yes_no(2,2)
Используется форма атрибута DIMENSION, когда одни и те же границы типы несколько массивов необходимо объявить. Используется вторая форма , когда несколько массивов разных типов и/или границ необходимо объявить. Третья форма смешивается из двух предыдущих: тип, DIMENSION(граница1) [, атрибут] :: a, b(граница2) где для а берется по умолчанию граница как граница1, но для b берется с другим явно определенным значением граница2. Смесь трех форм дана в следующей программе :
INTEGER, DIMENSION(8) :: x, y, z(16) REAL :: alpha(1:3), beta(4:9) REAL, DIMENSION(0:5,12:45, 6) :: data CHARACTER(len=10) :: names(25) Первый оператор объявляет x и y из 8 элементов каждый и z из 16 элементов. Второй оператор объявляет два массива одинакового типа, но с различными границами. Третий
оператор объявляет массив ранга 3. Последний оператор объявляет массив из 25 элементов, каждый элемент - это символьная строка длины 10. Можно включать массивы в качестве компонент производных типов данных и объявлять массивы производных типов данных:
TYPE point REAL :: position(3) END TYPE point TYPE(point) :: object(10) Тип point содержит 3 числа типа real, тогда массив object содержит 10 точек (элементов типа point), каждый из которых содержит 3 числа типа real.
Сечения массива Индивидуальные элементы Индивидуальные элементы и сечения массива дают уникальный идентификатор с помощью одного в ранге, отделенного запятыми. Каждый указатель может быть целым значением или выражением, дающим целое значение. REAL, DIMENSION(8) :: a INTEGER, DIMENSION(5,4) :: b ...
a(5) ! пятый элемент b(4,2) ! элемент на пересечении 4-й строки и 5-й колонки Указатели (такие как (i,j) ) отсылают к элементу на пересечении ряда i и колонки j, где i и j имеют целые значения в диапазоне между нижним и верхней границами в их измерениях. Используя выражение (например (2*k) ) обращаются к элементу, указвние которого дает результат оценки выражения. Результат выражения должен быть целым внутриобъявленных границ. Ошибкой будет ссылка вне диапазона границ массива (выше или ниже). Сечения Как возможно обратиться к индивидуальным жлементам массивов, также возможно в одном операторе обратиться и к нескольким эелементам (называемым сечением, вырезкой). Доступ
к сечению массива требует указания верхней и нижней границ сечения для спецификации вместе с шагом (для каждого измерения). Эта нотация называется тройкой индексов: array ([нижний]:[вехний][:шаг], ...) где нижний и вехний по умолчанию объявлены протяженностью, если они отсутствуют, и шаг по умолчанию равен 1. REAL, DIMENSION(8) :: a INTEGER, DIMENSION(5,4) :: b INTEGER :: i=3 ... a(3:5) ! элементы 3, 4, 5 a(1:5:2) ! элементы 1, 3, 5 b(1:2,2:i) ! элементы (1,2) (2,2) (1,3) и (2,3) b(i,1:4:2) ! элементы 1 и 3 из третьей строки b(2:4,1) ! элементы 2, 3 и 4 из первой колонки Границы в тройке индексов могут быть взяты любыми (целыми ) значениями. Использование тройки индексов компактно и удобный способ ссылки на произвольную секцию массива.
А(3:5) Некоторая более сложная секция массива дана ниже. Заметим, как нижние и верхние индексы могут быть опущены: REAL, DIMENSION(0:5) :: c INTEGER, DIMENSION(4:5) :: d c(:) ! весь массив c(:3) ! элементы 0,1,2,3 c(::2) ! элементы 0,2 и 4 d(:,4) ! все элементы четвертого столбца. d(::2,:) ! все элементы каждого второго ряда
A(1:5:2) Секция B(1:2,2:3)
Секция b(3,1:3:2)
Секция B(2:4,1)
Секция С(:)
Секция С(:3)
Секция С(::2)
Секция d(:,4)
Секция d(::2,:) Векторные указатели Векторные указатели - это целые массивы ранга 1, имеющие форму: (/ список /) где список это список указателей в произвольном порядке для ссылок. Рассмотрим следующий пример:
REAL, DIMENSION(9) :: a INTEGER, DIMENSION(3) :: random random=(/6,3,8/) ! установка значений для random a(random)=0.0 ! a(6)=a(3)=a(8)=0.0 a((/7,8,9/))=1.2 ! a(7)=a(8)=a(9)=1.2 Правильный векторный указатель (список) не должен содержать значения вне границ массива. Забота о том, чтобы не дублировать значение в векторном указателе, когда используют в присваивании LHS. Этого можно добиться для нескольких значений, записанных в один и тот же элемент массива: REAL, DIMENSION(5) :: a INTEGER, DIMENSION(3) :: list list=(/2,3,2/) a(list)=(/1.1, 1.2, 1.3/) ! неверно - элемент 2 устанавливается дважды
a((/0,2,4/)) = 0.0 !неверно - указатель вне границы Память для массива Физическая память: Как массив расположен в памяти, зависит от реализации в компьютере. Это обычно интересно для программиста. Упорядочение элементов массива: Ошибочно полагать, что два элемента массива следуют в памяти в точности по возрастанию их индексов. В действительности возможны многоразмерные массивы, размещенные в памяти как линейные последовательности элементов массива путем учета рангов, от наименьшего ранга заменой очередного. Эти способом матрица может быть запомнена по колонкам. Важно помнить об этом при манипуляциях и инициализации многоразмерных массивов.
Присваивание массива Присваивание всего массива Операции над всем массивом используются тогда, когда требуется присвоить элементам массива одно и то же значение (то есть скаляр) или когда копируются значения одного массива в другой. В первом случае скаляр распространяется во все элементы массива. В последнем - элементы массива эквивалентны один другому. Во всех случаях операнды в выражениях над массивами должны быть соответствующими: REAL, DIMENSION(100) :: a, b, c REAL : d(10,10) = 0.0 b = 2*a+4 a = 2.0 c = b*a c = d ! неверно - массивы не соответствуют Первое присваивание передает выражение над массивом в правой части оператора. Поскольку a и b соответствуют друг другу , это правильный оператор. Каждый элемент массива b вычисляется прибавлением числа 4 к соответствующему элементу массива a, умноженному на 2. Второе присваивание передает скаляр в правую часть - скаляры соответсвуют массивам в размерности и размерах. Каждый элемент массива a берется равным 2.0. Третье присваивание передает произведение массивов в правую часть. Поскольку массивы a и b соответствуют друг другу, их произведение можно вычислить - результат есть поэлементное произведение (не умножение матриц!). Результатом является новый массив, соответствующий массиву c. Таким образом, каждому элементу массива с присваивается произведение соответствующих элементов из a и b.
Присваивание секции (вырезки) массива Как полный массив может появляться в выражении, так же может появляться вырезка массива. И снова все вырезки массива в выражении должны соответствовать друг другу: REAL, DIMENSION(10) :: alpha, beta REAL :: gamma(20) ... alpha(1:5) = 2.0 ! всем первым 5 элементам присваивается 2.0 alpha(6:) = 0.0 ! элементы с 6 по 10 устанавливаются в 0.0 ... beta(1:10:2) = alpha(1:5)/6 ! соответствующие вырезки массива alpha(2:10) = alpha(1:9) gamma(11:20) = beta Последние три оператора имеют соответствующие вырезки массива по размеру (5, 9 и 10 элементов соотвестсвенно). Первый из них устанавливает каждый очередной элемент массива beta равным значению одного из пяти первых элементов массива alpha , деленным на 6 (beta(1)=alpha(1)/6, beta(3)=alpha(2)/6, etc.). Второй показывает мощную операцию, использующую массивы, где значения автоматически сдвинуты без использования оператора цикла DO. Элементы 2,3,...10 в массиве alpha принимают значения элементов 1,2,...9. Последнее присваивание демонстрирует другую важную концепсию. Тогда как массивы beta и gamma не соотвествуют, вырезка из gamma использована в выражении и имеется соответствие с beta. Это правильный оператор. Повторная нумерация Важно запомнить, что элементы в вырезке массива снова имеют в качестве меньшей границы 1 в каждом измерении. Независимо от представления элементов в исходном массиве, элементы в вырезке заново нумеруются так, что в каждом измерении индексы начинаются с 1. INTEGER :: nums(10), i nums = (/ 1,3,5,7,9,2,4,6,8,0 /) ... i = MAXLOC( nums ) ! i=5, элемент 9 есть maximum i = MAXLOC( nums(1:5) ) ! i=5, последний элемент в вырезке =9 i = MAXLOC( nums(3:8) ) ! i=3, третий элемент в вырезке =9 i = MAXLOC( nums(::2) ) ! i=3, третий элемент в вырезке =9
В вышеприведенном примере элемент со значением 9 есть максимальный элемент в массиве или вырезке. Тем не менее, его индекс заменяется как должно при перенумерации вырезки. Основные встроенные процедуры Основные встроенные процедуры специфицировны для скалярных аргументов, но могут брать аргументы-масивы:
REAL :: num, root REAL, DIMENSION(3,3) :: a INTEGER ::length(5) CHARACTER(LEN=7) :: c(5) ... root = SQRT(num) a = SQRT(a) length = LEN( TRIM(c) ) ! length=6 Функция SQRT() возвращает квадратный корень ее аргумента. Если аргумент - скалярная переменная, возвращается оно значение. Если аргумент массив, возвращается массив корней. Каждый элемент заменяется корнем существующего значения. Третье присваивание находит длину строки для каждого элемента массива с и отбрасывает завершающие пробелы. Следовательно, если c(1) есть 'Alexis ' функция TRIM() возвратит 'Alexis' (то есть удалит завершающий пробел), получит длину length 6.
Многие встроенные функции (включая математические функции) могут брать массивы и их вырезки или отдельные скалярные переменные в качестве аргументов. Массивы нулевого размера Массив в Fortran 90 может иметь размер 0. Это случается, когда нижняя граница больше верхней. Такой массив полезен, потому что не имеет значений элемента, не имеет данных, но правилен и определен. Пустой массив позволяет управлять известными ситуациями без требуемых экстра-кодов. Рассмотрим пример: INTEGER :: a(5)=(/1,2,1,1,3/) ... a(1:COUNT(a==1))=0 ! a=(/ 0,0,0,1,3 /) a(1:COUNT(a==4))=1 ! a не изменилось Первый оператор инициализирует a, используя конструктор массива.. Функция COUNT() возвращает число элементов, значения которых равно 1 и 4 соответственно. Первый оператор устанавливает a(1:3) в 0, но второй ничего (потому что нет элементов a, равных 4, и вырезка массива a(1:0) - массив размера 0). Допущение массивов размера 0 означает, что если массив или его вырезка (сечение) не имеет элементов, то оператор означает "не делать ничего" и программист избавлен от имеющихся проблем. Массивы и производные типы Так же как спецификация массивов встроенных типов данных, возможно включать спецификацию массивов как часть производных типов данных. Это облегчает группирование некоторых или многих экземпляров данных. Рассмотрим определение производного типа данных circle:
TYPE(circle) INTEGER :: radius REAL :: area END TYPE circle Где-нибудь в другом месте второй встроенный тип, называемый position , использован для запоминания координат центра окружности типа real; position имела два числа типа real в качестве компонент. Возможно заменить использование производного типа position массивом типа real : TYPE(circle) REAL, DIMENSION(2) :: pos INTEGER :: radius REAL :: area END TYPE circle Здесь pos(1) есть x координата, соответственно pos(2) есть y координата. Массив можно использовать, когда число компонент довольно велико. На компоненты массива ссылаются точно так же, как на другие компоненты: TYPE(circle) :: first ... first%pos(1) ! элемент 1 из pos first%pos(1:) ! весь массив (сечение)
Так же как несколько (или много) экземпляров (шаблонов, образцов) встроенных типов данных могут быть сгруппированы вместе как единый массив, так же возможно сгруппировать вместе экземпляры (шаблоны, образцы) производных типов данных. Например: TYPE(circle), DIMENSION(100) :: all_circles ... all_circles(1)%radius ! радиус окружности 1 all_circles(51:100)%radius ! радиус последней половины окружностей all_circles(1:100:2)%area ! область каждой другой окружности all_circles(:)%pos(1) ! x - координаты каждой окружности all_circles%pos ! все координаты всех окружностей all_circles(10)%pos(:) ! обе координаты 10-й окружности
Массив производного типа данных и/или компоненты массива имеют те же требования (то есть те же правила в выражениях и др.) и ограничения как другие массивы в Fortran 90. Например: TYPE(circle), DIMENSION(100) :: cir ...
cir(1:10) = cir(91:100) ! соответствие сегментов производных типов cir(i)%pos = cir(i-1)%pos(:) ! соответствие массивов типа real cir%pos(1:2) = cir(1:2)%pos ! ошибка, cir=cir(1:2) не соответствуют Нужно позаботиться , чтобы обеспечить, чтобы любая пометка секции массива была применена к правильной части выражения. Инициализация массивов Конструкторы Конструкторы используются для инициализации одномерных массивов, которые требуют фиксированных значений к началу программы. Конструктор это список, заключенный между двумя символами слэш // и круглыми скобками. Общая форма такова: array = (/ список /) где список может быть одним из следующих: список значений назначенного типа INTEGER :: a(6)=(/1,2,3,6,7,8/) Выражения для переменной (s) REAL :: b(2)=(/SIN(x),COS(x)/) Выражение от массива (s) INTEGER :: c(5)=(/0,a(1:3),4/) Неявный цикл DO REAL :: d(100)=(/REAL(i),i=1,100/) Конструктор может быть использован во время объявления как показано выше или в отдельном операторе. Массивы ранга 2 или более не могут быть инициализированы с помощью простого конструктора, а комбинацией конструктора (ов) и функции RESHAPE(). Reshape Функция RESHAPE() используется для инициализации и присваивания многомерных массивов (ранг выше 1). Ее можно использовать в операторе объявления или в отдельном операторе. Формат такой: RESHAPE (список, shape [,PAD] [,ORDER]) where список это одномерный массив или конструктор, содержащий данные и формирование одномерного массива или вектора или подпись вектора, содержащего новую форму данных. PAD - массив, содержащий данные для использования их в качестве простлойки в списке для требуемого формирования.. ORDER может быть использован для изменения порядка, в котором данные переформируются. Размер массива определяет размеры нового массива. Элементы определяют протяженность каждой размерности. В следующем примере:
INTEGER, DIMENSION(3,2) :: a a=RESHAPE((/0,1,2,3,4,5/),(/3,2/)) ! положить значения в a RESHAPE() генерирует массив ранга 2 с протяженностью 3 и 2 из списка значений в конструкторе. Поскольку этот массив подобен (соответствует) массиву a, использовано присваивание всего массива, дающее значение каждому элементу.. Аргумент ORDER не использован, значения из конструктора возвращены в порядке следования элементов в массиве, то есть a(1,1)=0, a(2,1)=1, a(3,1)=2, a(1,2)=3, и т д...
Оператор DATA Использование DATA, когда другие методы утомительны или / и невозможны: например, для инициализации более одного массива или вырезки массива. Формат такой: DATA variable / список / ... Например: INTEGER :: a(4), b(2,2), c(10) DATA a /4,3,2,1/ DATA a /4*0/ DATA b(1,:) /0,0/ DATA b(2,:)/1,1/ DATA (c(i),i=1,10,2/5*1/ DATA (c(i),i=2,10,2)/5*2/ Первый оператор DATA использует список значений, где значение для кажого элемента явно определено. Второй оператор DATA использует список для полного массива, где 4 - размер массива и 0 требуемое значение (* не есть умножение). Третий и четвертый операторы использует список в виде вырезки, где первый ряд принимает значения 0 и 0 и вторая строка принимает значения 1 и 1. Последние два оператора используют список, представленный неявным циклом DO, где элементам с нечетными индексами заданы значения 1 , элементам с четными индексами заданы значения 2. Вспомним, что оператор DATA: Можно располагать более чем в одной строке, но каждая строка должна иметь ключевое слово DATA Может быть использован для переменных как для массивов. WHERE
Оператор WHERE используется для управления, когда элементы массива использованы в операторе присваивания, зависящем от результата некоторого логического условия. Он имеет форму оператора или конструкции. Оператор WHERE допускает вычисление выражения элемента, зависящее от исходного значения этого элемента только если логическое условие верно. Синтаксис такой: WHERE (условие) выражение Рассмотрим следующее: INTEGER :: a(2,3,4) ... WHERE( a<0 ) a = 0 WHERE( a**2>10 ) a = 999 WHERE( a/=0 ) a = 1/a Первый оператор WHERE дает результат во всех отрицательных значениях a, устанавливая в 0; неотрицательные значения остаются нетронутыми. Второй оператор WHERE дает результат в элементах данных, устанавливаемых в 999, если их квадрат больше 10. Третий оператор вычисляет обратную величину для каждого элемента данных, за исключением тех, значения которых нулевые. Конструкция WHERE допускает присваивание массивов (снова в элементах базы) только если верно логическое условие, но должно предусмотреть альтернативное присваивание на случай "не истина".Синтаксис: WHERE (условие) block1 [ELSEWHERE block2] ENDWHERE В: INTEGER :: btest(8,8) ... WHERE ( btest<=0 ) btest = 0 ELSEWHERE btest = 1/btest ENDWHERE
Все элементы с отрицательными значениями в массиве btest устанавливаются в 0 и оставшиеся заменяются обратной величиной. Встроенные функции для массива Несколько встроенных функций имеется в Fortran90. Они позволяют сократить время и усилия в процессе программирования. Их можно разделить на 7 разделов:
Умножение векторов и матриц Упрощение Исследование Конструирование Реорганизация Манипуляции Локализация (указание ) Пример упрощения Встроенная функция ALL() имеет вид: ALL( условие ) И определяет, когда для всех элементов в массиве справедливо условие (равно) .TRUE. . Результат - логическое значение .TRUE. если все элементы удовлетворяют условию и.FALSE. в противном случае. LOGICAL :: test, test2, test3 REAL, DIMENSION(3,2) :: a a = RESHAPE( (/5,9,6,10,8,12/), (/3,2/) ) ... test = ALL( a>5 ) ! ложь test2 = ALL( a<20 ) ! истина test3 = ALL( a>=5 .AND. test2 ) ! истина (true) Первый оператор возвращает .FALSE. поскольку первый элемент равен 5, но не больше 5. Второй оператор возвращает .TRUE. поскольку все элементы имеют значения, меньшие 20. Третий Второй оператор возвращает .TRUE. поскольку все элементы имеют значения, равные или большие 5 , и значение переменной test2 равно .TRUE. . Я Пример исследования (запроса) Встроенная функция SIZE() имеет вид:
SIZE( массив [, DIM] ) И возвращает протяженность массива для указанного измерения (заданного аргументом DIM). Если измерение не задано, SIZE() возвращает число всех элементов в массиве.
REAL, DIMENSION(3,2) :: a num=SIZE(a) ! num=6 num=SIZE(a,DIM=1) ! num=3 num=SIZE(a,DIM=2) ! num=2 Значение, заданное аргументу DIM указывает измерение - DIM=1 возвращает число рядов, DIM=2 число столбцов и т.д.
DIM=2
DIM=1 Я Пример конструирования Встроенная функция SPREAD() имеет вид: SPREAD(массив, DIM, NCOPIES) И копирует данный массив добавлением измерения, где DIM становится измерением и NCOPIES числом копий. REAL, DIMENSION(3) :: a=(/2,3,4/)
REAL, DIMENSION(3,3) :: b,c b=SPREAD(a, DIM=1, NCOPIES=3) c=SPREAD(a, DIM=2, NCOPIES=3) Первый оператор SPREAD копирует массив a три раза в измерении ряда. Второй оператор SPREAD копирует массив a три раза в измерении столбца. Я Пример локализации Встроенная функция MAXLOC() имеет вид: MAXLOC(массив, [маска]) И определяет положение элемента, который имеет наибольшее значение в массиве и удовлетворяет маске (необязательной). Маска это логический массив (или условие , включенной в массив), которое соответствует массиву. Только элементы массива , которые составляют часть в поиске максимальных по значению элементов, связываются с элементами.TRUE. в маске. REAL :: a(5)=(/2,8,5,3,4/) num = MAXLOC( a ) ! num=2 num = MAXLOC( a(2:4) ) ! num=1, заметим перенумерацию num = MAXLOC( a, MASK=a<5 ) ! num=5 Для третьего оператора a 2
8
5
3
4
F
T
T
MASK=a<5 T
F
Выбираются 2
8
4
Первый оператор возвращает 2, поскольку это позиция наибольшего номера в списке. Второй оператор MAXLOC() возвращает значение 1, поскольку это позиция наибольшего по значению элемента в вырезке массива (элемента в вырезке массива перенумерованы с 1 как меньшей границы по каждому измерению). Третий оператор MAXLOC() возвращает 5 , поскольку это позиция наибольшего номера в списке, где числа, большие 5, исключаются маской. Я Условные операторы Выбор и направляющее управление через подходящий путь доступа к программе еть очень мощная и нужная операция. Fortran 90 предоставляет два механизма, которыми программист осуществляет выбор альтернативных действий, зависящих от условий: •оператор и конструкция IF •конструкция выбора CASE В управлении порядком выполнения операторов используется оператор GOTO. Оператор и конструкция IF Простейшая форма оператора IF это единственное действие, основанное на единственном условии: IF( выражение ) оператор Только если выражение (логическая переменная или выражение) имеет значение .TRUE. оператор выполним. Например, в: IF( x<0.0 ) x=0.0 если x меньше чем нуль, то дается новое значение (0.0); в противном случае x сохраняет свое прежнее значение. Структура конструкции IF зависит от числа условий , которые могут быть проверены и имеет следующую общую форму: [имя:] IF (выражение1) THEN блок1 ELSEIF (выражение 2) THEN [имя] блок 2
... [ELSE [имя] блок] ENDIF [имя] Где выражение№ есть логическая переменная или выражение. Конструкция используется, когда число операторов зависит от условия. Например, 'если идет дождь, то взять пиджак и взять зонт'. Это требуется в части THEN . Заметим, что оператор END IF (или ENDIF) требуется, чтобы обозначить конец условного блока в операторе. LOGICAL :: rain INTEGER :: numb=0, ncoat ... IF ( rain ) THEN ncoat = 1 numb = numb+1 ENDIF If дождь есть .TRUE. , блок операторов выполним и управление переходит к следующему оператору после ENDIF; в противном случае, блок операторов опускается и управление переходит прямо к следующему оператору после ENDIF. Более сложная ситуация может возникать, когда представляющая альтернативные действия зависимость в единственном условии. Для примера, предыдущие примеры не делают различий между уровнями ливня . Пример выше может быть перефразирован 'если здесь легкий дождь, то взять пиджак, в противном случае (иначе) взять пиджак и зонт'. REAL :: inches_of_rain INTEGER :: numb=0, ncoat ... IF( inches_of_rain>0.05 ) THEN ! сильный дождь ncoat = 1 numb = numb+1
ELSE ! легкий дождь ncoat = 1 ENDIF Заметим, используется часть ELSE, отделяющей различные варианты, и что and каждый блок может содержать один или более операторов. Второй блок операторов выполняет установку операторов "по умолчанию" для случая, когда условие не выполняется. Переход управления следует тем же правилам как упомянуто выше. Здесь рассмотрена ситуация, когда альтернативные действия зависят от нескольких условий. Например, скидка (discount) , представляемая для покупки, может очень зависеть от числа покупаемых лотов - больше покупок, больше скидка: REAL :: cost, discount INTEGER :: n ! число покупок ... IF ( n>10 ) THEN ! 25% скидка на 11 или более discount = 0.25 ELSEIF ( n>5 .AND. n<=10 ) THEN ! 15% скидка на 6-10 покупок discount = 0.15 ELSEIF ( n>1 .AND. n<=5 ) THEN ! 15% скидка на 2-5 покупок discount = 0.1 ELSE ! без скидки для 1 покупки discount = 0.0 ENDIF cost = cost-(cost*discount) WRITE(*,*) `Накладная для ', cost
Заметим, что ELSEIF используется для добавления новых условий к блоку (другая скидка используется). Оператор ELSE действует как умолчание в случае защиты от других событий. Снова те же правила перехода управления.
Конструкция IF мжет быть помечена. Именованная конструкция может быть использована , когда одна находится внутри другой. Этот способ пометки делает программу легкой для понимания: outer: IF( x==0 ) THEN ... ELSE outer inner: IF( y==0.0 ) THEN ... ENDIF inner ENDIF outer Я Конструкция SELECT CASE Конструкция SELECT CASE обеспечиввает альтернативу серии повторяющихся операторов IF ... THEN ... ELSE IF. Ее общий вид таков: [имя:] SELECT CASE( выражение ) CASE( значение ) [имя] блок ... [CASE DEFAULT блок] END SELECT [имя] Результат выражения может быть типа CHARACTER, LOGICAL или INTEGER; значение должно быть того же типа, что и результат выражения и может быть любой комбинацией из: •единичной integer, character, или logical, связывающим в типе •min : max любого значеиия между двумя ограничениями
•min: любое значение минимального значения из вышеприведенных •:max любое значение выше минимального значения
CASE DEFAULT не обязателен и закрывает все другие возможные значения выражения, еще не закрытые другими операторами CASE. Например: INTEGER :: month ... season: SELECT CASE( month ) CASE(4,5) ! months 4 и 5 WRITE(*,*) 'Spring' CASE(6,7) ! months 6 и 7 WRITE(*,*) 'Summer' CASE(8:10) ! months 8,9 и 10 WRITE(*,*) 'Autumn' CASE(11,1:3,12) ! months 1,2,3,11,12 WRITE(*,*) 'Winter' CASE DEFAULT ! целое вне диапазона 1-12 WRITE(*,*) 'not a month' END SELECT season Приведенный выше пример печатает сезон года, ассоциированный с заданным месяцем. Если значение целого месяца не лежит в диапазоне 1-12, выбирается вариант умолчания и печатается сообщение об ошибке 'not a month'; в других случаях выбирается один из операторов CASE. Заметим, что они не определяют порядок значеиий в операторе CASE. Я
GOTO Оператор GOTO можно использовать дле передачи управления другому оператору в виде: GOTO label
Оператор GOTO просто передает управление оператору, помеченному числовой меткой, перепрыгивая любые операторы между ними. Например: ... IF( x<10 ) GOTO 10 ... 10 STOP
Оператор GOTO можно бы не применять, где возможно, программы , содержащие такие оператороы, заведомо трудно поддерживать. Оператор STOP завершает программу. Я Операторы управления Fortran 90 имеет три главных типа структур управления • • •
IF CASE DO
Каждая конструкция может быть вложена в другую и может быть именована для улучшения читаемости программы. Условные операторы Выбор и направляющее управление через подходящий путь доступа к программе еть очень мощная и нужная операция. Fortran 90 предоставляет два механизма, которыми программист осуществляет выбор альтернативных действий, зависящих от условий: •оператор и конструкция IF •конструкция выбора CASE В управлении порядком выполнения операторов используется оператор GOTO. Оператор и конструкция IF
Простейшая форма оператора IF это единственное действие, основанное на единственном условии: IF( выражение ) оператор Только если выражение (логическая переменная или выражение) имеет значение .TRUE. оператор выполним. Например, в: IF( x<0.0 ) x=0.0 если x меньше чем нуль, то дается новое значение (0.0); в противном случае x сохраняет свое прежнее значение. Структура конструкции IF зависит от числа условий , которые могут быть проверены и имеет следующую общую форму: [имя:] IF (выражение1) THEN блок1 ELSEIF (выражение 2) THEN [имя] блок 2 ... [ELSE [имя] блок] ENDIF [имя] Где выражение№ есть логическая переменная или выражение. Конструкция используется, когда число операторов зависит от условия. Например, 'если идет дождь, то взять пиджак и взять зонт'. Это требуется в части THEN . Заметим, что оператор END IF (или ENDIF) требуется, чтобы обозначить конец условного блока в операторе. LOGICAL :: rain INTEGER :: numb=0, ncoat ... IF ( rain ) THEN ncoat = 1
numb = numb+1 ENDIF If дождь есть .TRUE. , блок операторов выполним и управление переходит к следующему оператору после ENDIF; в противном случае, блок операторов опускается и управление переходит прямо к следующему оператору после ENDIF. Более сложная ситуация может возникать, когда представляющая альтернативные действия зависимость в единственном условии. Для примера, предыдущие примеры не делают различий между уровнями ливня . Пример выше может быть перефразирован 'если здесь легкий дождь, то взять пиджак, в противном случае (иначе) взять пиджак и зонт'. REAL :: inches_of_rain INTEGER :: numb=0, ncoat ... IF( inches_of_rain>0.05 ) THEN ! сильный дождь ncoat = 1 numb = numb+1 ELSE ! легкий дождь ncoat = 1 ENDIF Заметим, используется часть ELSE, отделяющей различные варианты, и что and каждый блок может содержать один или более операторов. Второй блок операторов выполняет установку операторов "по умолчанию" для случая, когда условие не выполняется. Переход управления следует тем же правилам как упомянуто выше. Здесь рассмотрена ситуация, когда альтернативные действия зависят от нескольких условий. Например, скидка (discount) , представляемая для покупки, может очень зависеть от числа покупаемых лотов - больше покупок, больше скидка: REAL :: cost, discount INTEGER :: n ! число покупок ... IF ( n>10 ) THEN ! 25% скидка на 11 или более
discount = 0.25 ELSEIF ( n>5 .AND. n<=10 ) THEN ! 15% скидка на 6-10 покупок discount = 0.15 ELSEIF ( n>1 .AND. n<=5 ) THEN ! 15% скидка на 2-5 покупок discount = 0.1 ELSE ! без скидки для 1 покупки discount = 0.0 ENDIF cost = cost-(cost*discount) WRITE(*,*) `Накладная для ', cost
Заметим, что ELSEIF используется для добавления новых условий к блоку (другая скидка используется). Оператор ELSE действует как умолчание в случае защиты от других событий. Снова те же правила перехода управления. Конструкция IF мжет быть помечена. Именованная конструкция может быть использована , когда одна находится внутри другой. Этот способ пометки делает программу легкой для понимания: outer: IF( x==0 ) THEN ... ELSE outer inner: IF( y==0.0 ) THEN ... ENDIF inner ENDIF outer Я Конструкция SELECT CASE
Конструкция SELECT CASE обеспечиввает альтернативу серии повторяющихся операторов IF ... THEN ... ELSE IF. Ее общий вид таков: [имя:] SELECT CASE( выражение ) CASE( значение ) [имя] блок ... [CASE DEFAULT блок] END SELECT [имя] Результат выражения может быть типа CHARACTER, LOGICAL или INTEGER; значение должно быть того же типа, что и результат выражения и может быть любой комбинацией из: •единичной integer, character, или logical, связывающим в типе •min : max любого значеиия между двумя ограничениями •min: любое значение минимального значения из вышеприведенных •:max любое значение выше минимального значения
CASE DEFAULT не обязателен и закрывает все другие возможные значения выражения, еще не закрытые другими операторами CASE. Например: INTEGER :: month ... season: SELECT CASE( month ) CASE(4,5) ! months 4 и 5 WRITE(*,*) 'Spring'
CASE(6,7) ! months 6 и 7 WRITE(*,*) 'Summer' CASE(8:10) ! months 8,9 и 10 WRITE(*,*) 'Autumn' CASE(11,1:3,12) ! months 1,2,3,11,12 WRITE(*,*) 'Winter' CASE DEFAULT ! целое вне диапазона 1-12 WRITE(*,*) 'not a month' END SELECT season Приведенный выше пример печатает сезон года, ассоциированный с заданным месяцем. Если значение целого месяца не лежит в диапазоне 1-12, выбирается вариант умолчания и печатается сообщение об ошибке 'not a month'; в других случаях выбирается один из операторов CASE. Заметим, что они не определяют порядок значеиий в операторе CASE. Я GOTO Оператор GOTO можно использовать дле передачи управления другому оператору в виде: GOTO label
Оператор GOTO просто передает управление оператору, помеченному числовой меткой, перепрыгивая любые операторы между ними. Например: ... IF( x<10 ) GOTO 10 ... 10 STOP
Оператор GOTO можно бы не применять, где возможно, программы , содержащие такие оператороы, заведомо трудно поддерживать. Оператор STOP завершает программу.
Я Конструкция SELECT CASE Конструкция SELECT CASE обеспечиввает альтернативу серии повторяющихся операторов IF ... THEN ... ELSE IF. Ее общий вид таков: [имя:] SELECT CASE( выражение ) CASE( значение ) [имя] блок ... [CASE DEFAULT блок] END SELECT [имя] Результат выражения может быть типа CHARACTER, LOGICAL или INTEGER; значение должно быть того же типа, что и результат выражения и может быть любой комбинацией из: •единичной integer, character, или logical, связывающим в типе •min : max любого значеиия между двумя ограничениями •min: любое значение минимального значения из вышеприведенных •:max любое значение выше минимального значения
CASE DEFAULT не обязателен и закрывает все другие возможные значения выражения, еще не закрытые другими операторами CASE. Например: INTEGER :: month ... season: SELECT CASE( month )
CASE(4,5) ! months 4 и 5 WRITE(*,*) 'Spring' CASE(6,7) ! months 6 и 7 WRITE(*,*) 'Summer' CASE(8:10) ! months 8,9 и 10 WRITE(*,*) 'Autumn' CASE(11,1:3,12) ! months 1,2,3,11,12 WRITE(*,*) 'Winter' CASE DEFAULT ! целое вне диапазона 1-12 WRITE(*,*) 'not a month' END SELECT season Приведенный выше пример печатает сезон года, ассоциированный с заданным месяцем. Если значение целого месяца не лежит в диапазоне 1-12, выбирается вариант умолчания и печатается сообщение об ошибке 'not a month'; в других случаях выбирается один из операторов CASE. Заметим, что они не определяют порядок значеиий в операторе CASE. Я GOTO Оператор GOTO можно использовать дле передачи управления другому оператору в виде: GOTO label
Оператор GOTO просто передает управление оператору, помеченному числовой меткой, перепрыгивая любые операторы между ними. Например: ... IF( x<10 ) GOTO 10 ... 10 STOP
Оператор GOTO можно бы не применять, где возможно, программы , содержащие такие оператороы, заведомо трудно поддерживать. Оператор STOP завершает программу. Я **************** DO ************************* http://parallel.uran.ru/alpha/f90www/loops_rus.htm Вложения Размещение конструкции одного блока внутри другого (операторы IF-THEN внутри цикла DO, и т.д.) возможно, но внутренний блок (блоки) должен быть окаймлен наружным (и) блоком (ами) с помощью соответствующих операторов. Запрещено перекрывать вложенные операторы. Например:
main: IF( sum>100 ) THEN inner: DO i=1,n ... ENDIF main ENDDO inner ! запрещено, inner должен быть внутри main
Это в общем верно для всех конструкций, то есть для цикла DO , конструкций IF-THENELSEIF и CASE . Максимальный уровень вложения (конструкции внутри конструкций внутри конструкций ...) зависит от компилятора. Структура программы
Отдельная программа на Fortran 90 может быть сделана из нескольких отдельных программных модулей, называемых процедурами (внутренняя - internal, внешняя - external или модуль module)или модулями. Выполняемая программа содержит одну главную программу и некоторое число (может быть 0) других программных модулей. Важно реализовать так, чтобы внутренние детали каждого программного модуля отделены от других модулей. Только связь между модулями образует интерфейс, в котором один модуль вызывает другой по имени. Главное в написании программ с помощью программных модулей обеспечить, чтобы существовал процедурный интерфейс.
Деление программы на модули имеет некоторые преимущества: • • •
•
программные модули могут быть написаны и отлажены независимо. программный модуль, который имеет хорошо определенную задачу, легче понять и построить. Разработанные и отлаженные модули и внешние процедуры могут быть повторно использованы в других программах (программист может построить личные библиотеки) Хороший компилятор может лучше оптимизировать код благодаря модульной форме.
Порядок операторов Fortran предписывает некоторый порядок операторов в программе. Любая программа или процедура должна быть написана по следующим правилам: 1. Заголовок программы должен быть первым оператором (PROGRAM, FUNCTION or SUBROUTINE). Оператор PROGRAM не обязятелен, но рекомендуется к использованию. 2. Все операторы спецификации должны предшествовать первому выполняемому оператору. Даже с помощью операторов DATA могут быть размещены с выполняемым текстом, то они должны лежать в области определения. Также хорошая идея группировать операторы FORMAT statements вместе для ясности. 3. Выполняемые операторы должны следовать в порядке, определяемом логикой программы. 4. Программа или процедура должна заканчиваться операторм END. Внутри множества операторов спецификации требуется относительно малое упорядочение, тем не менее, в общем если один объект использован в спецификации другого, то естественно требуется, чтобы он был определен раньше. Другими словами, именованные константы (PARAMETER s) должны быть объявлены прежде, чем они могут быть использованы в качестве части объявления другого объекта. Следующая таблица детализует порядок следования: Упорядочение операторов
Операторы PROGRAM, FUNCTION, SUBROUTINE, MODULE или BLOC DATA Оператор USE IMPLICIT NONE
ENTRY
Оператор PARAMMETER
Операторы IMPLICIT
Операторы PARAMMETER и DATA
Определение производных типов, блоки интерфейса, операторы определения типа, операторы функций-операторов и операторы спецификаций
Операторы DATA
Вычисляемые конструкции
Оператор CONTAINS Внутренние процедуры или процедуры модуля Оператор END
Выполнение программы начинается с первого выполняемого оператора главной программы (MAIN PROGRAM, где процедура, вызванная при выполнении, начинается с первого выполняемого оператора после вызванной точки входа. Невыполняемые операторы концептуально `выполнены' (!) моделированием инициализации программы, иными словами, они вызываются тогда и только тогда, когда выполняется начало главной программы. Теперь объясним таблицу, • • •
•
•
Может быть только 1 главная программа PROGRAM, Может быть много программных единиц FUNCTION s и SUBROUTINE s (процедур) с уникальными именами. Может быть некоторое число модулей MODULE s с уникальными именами, которые связаны с программой с помощью оператора USE. Модули - очень легко приспособляемые программные единицы и используют в пакете несколько средств (например, процедуры, определения типов, объявления объектов или семантические расширения). Используется очень много поддержки и замены для средств языка FORTRAN 77. Может быть только одна подпрограмма BLOCK DATA -- они не описаны как часть курса -- это возможность определить глобальные инициализации и глобальные константы и лучше использовать операторы MODULE и USE. Оператор USE: Он подключает модуль, связывет объекты модуля с программным модулем, делая их доступными. Когда использует его содержимое public, оно становится лоступным как будто бы оно было объявлено непосредственно и явно в самой
программной единице. Модули могут быть предварительно скомпилярованы (подобно библиотекам) или быть написаны программистом. Любые глобальные объекты могут быть поставлены в модуль и использованы, когда это потребуется. • •
•
Оператор IMPLICIT NONE должен стоять после оператора USE. Его использование настоятельно предлагается. Операторы FORMAT и ENTRY. Операторы Format должны быть сгуппированы вместе в одном месте кода. Операторы ENTRY реализуют механизм `вход в' середину процедуры. Его использование противоречит хорошему стилю и сильно не рекомендуется в связи с его естественной опасностью. Операторы PARAMETER и IMPLICIT: Оператор IMPLICIT не могут быть использованы, IMPLICIT NONE можно использовать только в форме рассмотриваемых неявных типов. (операторы IMPLICIT разрешают пользователю переопределять явно типы объектов по первой букве. Нельзя иметь оператор, если есть строка IMPLICIT NONE.) Операторы PARAMETER, предполагает, что использован атрибутный стиль (Fortran 90) объявления PARAMETER.
•
•
Оператор DATA может быть размещен в объявлении (но не в выполнеии), общая практика состоит в их расположении после объявлений перед выполняемыми операторами. Блоки интерфейса обычно размещены в заголовке объявлений и сгруппированы вместе. Операторы-функции являются формой однострочного объявления оператора, взамен могут быть использованы внутренние процедуры
• • • •
Выполняемые операторы это вещи, подобные операторам DO, конструкциям IF и операторам присваивания. CONTAINS отделяет главную программную единицу ``main'' от всех локальных видимых внутренних процедур. внутренние процедуры следуют тому же порядку как и нормальные процедуры, исключая возможность содержать внутри себя внутренние процедуры второго уровня. оператор END ограничитель, завершающий текущую программную единицу.
Синтаксис программы Main
Все программы имеют одну (и толко одну) главную программу main. Выполнение программы всегда начинается с первого оператора в главной программе main программной единицы main и продолжается из нее. Общая форма программной единицы main такова:
PROGRAM [имя] [операторы спецификации (определений)] [выполняемые операторы ] ... [CONTAINS внутренние процедуры] END [PROGRAM [имя]]
Оператор PROGRAM помечает начало главной программной единицы, а оператор END PROGRAM не только помечает конец программной единицы, но и конец всей программы. Использование имени программы не обязательно, но полезно. Оператор CONTAINS служит идентификации тех процедур, которые расположены внутри главного программного модуля. Когда все выполняемые операторы закончены, управление переходит через внутренние процедуры к оператору END.
Программа может быть закончена в любой точке во время своего выполнения (и в любой программной единице) с помощью оператора STOP:
STOP [метка]
где метка - необязательная символьная строка в скобках, которая может быть использована для информирования пользователя, как и в какой точке остановлена программа.
Пример структуры программы Main
PROGRAM Main ! .. CONTAINS ! Внутренние процедуры
SUBROUTINE Sub1 (..) ! Вычисляемые операторы END Sub1 ! и т .д. FUNCTION funkyn( ..) ! Вычисляемые операторы END FUNCTION funkyn END PROGRAM Main
Пример программы
PROGRAM Main IMPLICIT NONE REAL :: x READ*, x PRINT*, FLOOR(x) ! Intrinsic PRINT*, Negative(x) CONTAINS REAL FUNCTION Negative(a) REAL, INTENT(IN) :: a Negative = -a END FUNCTION Negative END PROGRAM Main Формат представления программы
Рассмотрим пример программы, показывающий формат представления программы
PROGRAM circle_area IMPLICIT NONE ! читает значение, представляющее радиус окружности, ! затем вычисляет и выводит площадь круга. ! REAL :: radius, area REAL, PARAMETER :: pi=3.141592 ! READ (5,*) radius area = pi*radius*radius ! вычисляет площадь WRITE (6,*) area END PROGRAM circle_area
Укажем несколько особенностей: •Исходный код начинается с оператора PROGRAM, в котором дано имя circle_area. Исходный код заканчивается оператором END PROGRAM (который включает имя программы). Все операторы в программе набраны с отступом для лучшей читаемости. • Имеется объяснение программы , оно дано в начале и в теле кода программы main , в форме оператора комментария. Это улучшает читаемость. •Оператор IMPLICIT NONE стоит первым в программе и влияет на определения следующих переменных. Определения переменных сгруппированы вместе и появляются перед всеми выполняемыми операторами, такими как операторы присваивания и операторы ввода-вывода. •Строки пробелов использованы для выделения секций программы для читабельности.
В основном программы будут скомпонованы по одному оператору в строке. Тем не менее существует ограничение на число символов в строке (132), (в зависимости от редактора и размера экрана, который вы используете, чаще всего максимум 80 символов в строке ). Оператор, который превышает лимит строки, может быть продолжен на следующую строку с помощью символа амперсанда & в конце продолжаемой строки. Строка не может быть прервана в произвольной точке, но в подходящем месте.
WRITE (6,*) temp_value, pi*radius*radius, & length, breadth
Более одного оператора может быть размещено в одной строке при их разделении символом "точка с запятой" (;) .
length=10.0; breadth=20.0; area=length*breadth
Не рекомендуется размещать более одного оператора в строке в тех случаях, когда операторы длинные. Программные единицы
Уровни доступа
Входные-выходные параметры
Использование модуля
Дополнения
Модуль
PRIVATE и PUBLIC
В дополнение к четырем старым программным единицам: PROGRAM (которая является главной программой), SUBROUTINE, FUNCTION и BLOCK DATA, добавлена новая конструкция MODULE , которая хорошо вкладывает новый смысл в старые понятия.. Подпрограмма - это общее понятие для подпрограммы SUBROUTINE и функции FUNCTION. Уровни доступа
Снова хочу подчеркнуть, что все программные Fortran 77 размещены по существу на одном и том же уровне, даже если главная программа логически стоит выше подпрограмм и функций, которые она вызывает. В действительности BLOCK DATA на более высоком уровне и все другие программные модули с точки зрения системы Fortran на том же уровне с главной программой, чуть-чуть немного выше. Исключение составляют так называемые операторыфункции, определения которых стоят первыми в программной единице, сразу после определений, и они являются внутренними для этой программной единицы и поэтому логически ниже по уровню. К сожалению, типичный программист на Fortran 77 не может использовать операторы-функции.
Вышесказанное означает, что имена всех программ на одном и том же логическом уровне, которое означает, что две различные процедуры и две различных части большой программы не могут иметь одинаковые имена. Часто численные и графические библиотеки включают тысячи функций и подпрограмм, а имя каждой программы состоит из более чем 6 символов по старому стандарту Fortran . Поэтому имеется большой риск конфликта имен. Эта проблема может быть частично решена с помощью функций-операторов, поскольку они внутренние по отношению к рассматриваемой программной единице , и поэтому различные функцииоператоры могут иметь одни и те же имена в разных программных единицах. Недостаток в том, что они могут встретиться только в одной строке программы. Но они могут вызывать все другие и таким образом далее функции-операторы могут вызывать более ранние функцииоператоры, но не наоборот. Я Дополнения F90 добавляет внутренние функции и подпрограммы, предоставляя большую свободу. Они определяются в конце каждого программной единицы (но не в BLOCK DATA) после новой команды CONTAINS и перед END. Внутренняя подпрограмма может иметь доступ к именам программной единицы, включая вызовы других внутренних подпрограмм программной единицы. Она пишется как обычная подпрограмма, но она уже не может иметь своих внутренних функций или подпрограмм.. Внутренняя функция это более мощная замена для функций-операторов.
Обычные функции и подпрограммы остаются теми же самыми как и внешние подпрограммы и внешние функции, но они теперь должны иметь более существенные доводы для имен, чем прежде, покольку вы теперь имеете также и внешние подпрограммы. Прежде вы имели только встроенные функции как альтернативу при построении программы. В дополнение число встроенных функций значительно возросло и незначительно могут быть добавлены встроенные подпрограммы. Я Спецификация входных-выходных параметров
Для каждого аргумента в спецификации переменных подпрограммы мы можем теперь дать его вид INTENT как IN, OUT или INOUT. Если задан IN, то фактический параметр может быть выражением подобным X+Y или SIN(X) или константой как 37, после чего значение может быть передано подпрограмме, но новое значение не может быть возвращено вызывающей единице. Переменной в этом случае может быть присвоено новое значение в подпрограмме. Если задан OUT, в другом случае, то фактический параметр может быть переменной. На входе в подпрограмму в этом случае переменная не определена. Третий случай, когда возможно одно значение на входе и другое на выходе или то же самое значение. В этом случае фактический параметр должен быть переменной. Если переменная имеет атрибут указателя, то INTENT не должен задаваться. Реализован INTENT еще не всеми компиляторами. Я Модуль Одно из возможных использований программной единицы MODULE состоит заботе о глобальных данных. Как замена для BLOCK DATA. Другое использование в создании пакета новых типов данных. Примеролм может служить пакет интервальной арифметики. Связывая с каждым значением X вы имеете интервал (X_lower; X_upper). Используя пакет вы будете иметь только имя переменной X в смысле интервала. Переменная X теперь поддержана новым типом данных "интервал". Вот из файла интервальной арифметики file interval_arithmetics.f90 или intv_ari.f90.
MODULE INTERVAL_ARITHMETICS TYPE INTERVAL REAL LOWER, UPPER END TYPE INTERVAL INTERFACE OPERATOR (+) MODULE PROCEDURE INTERVAL_ADDITION END INTERFACE INTERFACE OPERATOR (-) MODULE PROCEDURE INTERVAL_SUBTRACTION END INTERFACE INTERFACE OPERATOR (*)
MODULE PROCEDURE INTERVAL_MULTIPLICATION END INTERFACE INTERFACE OPERATOR (/) MODULE PROCEDURE INTERVAL_DIVISION END INTERFACE CONTAINS FUNCTION INTERVAL_ADDITION(A, B) TYPE(INTERVAL), INTENT(IN) :: A, B TYPE(INTERVAL) :: INTERVAL_ADDITION INTERVAL_ADDITION%LOWER = A%LOWER + B%LOWER INTERVAL_ADDITION%UPPER = A%UPPER + B%UPPER END FUNCTION INTERVAL_ADDITION
FUNCTION INTERVAL_SUBTRACTION(A, B) TYPE(INTERVAL), INTENT(IN) :: A, B TYPE (INTERVAL) :: INTERVAL_SUBTRACTION INTERVAL_SUBTRACTION%LOWER = A%LOWER - B%UPPER INTERVAL_SUBTRACTION%UPPER = A%UPPER - B%LOWER END FUNCTION INTERVAL_SUBTRACTION
FUNCTION INTERVAL_MULTIPLICATION(A, B) ! POSITIVE NUMBERS ASSUMED TYPE(INTERVAL), INTENT(IN) :: A, B TYPE (INTERVAL) :: INTERVAL_MULTIPLICATION
INTERVAL_MULTIPLICATION%LOWER = A%LOWER * B%LOWER INTERVAL_MULTIPLICATION%UPPER = A%UPPER * B%UPPER END FUNCTION INTERVAL_MULTIPLICATION FUNCTION INTERVAL_DIVISION(A, B) ! POSITIVE NUMBERS ASSUMED TYPE(INTERVAL), INTENT(IN) :: A, B TYPE(INTERVAL) :: INTERVAL_DIVISION INTERVAL_DIVISION%LOWER = A%LOWER / B%UPPER INTERVAL_DIVISION%UPPER = A%UPPER / B%LOWER END FUNCTION INTERVAL_DIVISION END MODULE INTERVAL_ARITHMETICS
Компиляция выше приведенного приведет к созданию файла interval_arithmetics.mod. Этот файл включает интересную модифицированную версию вышеприведенного кода. Любая программа, которая будет использовать этот пакет, должна содержать оператор USE INTERVAL_ARITHMETICS в начале операторов спецификации. Тогда будут доступны тип данных INTERVAL и четыре арифметических действия с этим типом. В некоторых случаях желательно только включить некоторые средства в модуль. В таком случае вы можете использовать атрибут ONLY внутри оператора USE. USE module_name, ONLY : список_выбранных_подпрограмм Я Использование модуля Ниже пример очень простой главной программы для тестирования интервальной арифметики . Он из файла interval.f90 или intv.f90. USE INTERVAL_ARITHMETICS IMPLICIT NONE TYPE (INTERVAL) :: A, B, C, D, E, F A%LOWER = 6.9
A%UPPER = 7.1 B%LOWER = 10.9 B%UPPER = 11.1 WRITE (*,*) A, B C=A+B D=A-B E=A*B F=A/B WRITE (*,*) C, D WRITE (*,*) E, F END
Запуская эту программу на компьютере Sun с компилятором NAG получили следующий результат на выводе:
% f90 interval_arithmetics.f90 interval.f90 interval_arithmetics.f90: interval.f90: % a.out 6.9000001 7.0999999 10.8999996 11.1000004 17.7999992 18.2000008 -4.2000003 -3.7999997 75.2099991 78.8100052 0.6216216 0.6513762 % exit
Мы компилировали программу компилятором f90, и счетная программа была автоматически названа a.out. Прямо с выше приведенным (первый модуль) компиляция работала с SunSoft и компилятором Digital compilers! Я PRIVATE и PUBLIC В модуле некоторые понятия могут быть определены как PRIVATE, которое означает, что программная единица закрывает использования этого понятия извне. В противовес этому может быть использовано явное описание PUBLIC, обычно PUBLIC имеет место по умолчанию. Следующие операторы PRIVATE PUBLIC :: VAR1 Объявляют все переменные локальными, но VAR1 глобальный. Заметим, что оба понятия (PUBLIC и PRIVATE) могут быть даны как оператор, например
INTEGER :: IVAR PRIVATE :: IVAR
Или как атрибут INTEGER, PRIVATE :: IVAR Я Процедуры Описание всех сторон понятия "процедура" включает следующие разделы
Фактические и формальные Переменные в процедуре параметры
Параметры процедур
Внутренние процедуры
SAVE
Объекты образа
Внешние процедуры
Локальные объекты
Вид параметра INTENT)
допустимого (атрибут
Подпрограммы и функции
Соответствие параметров
Ключевые параметры
Subroutine
Правила области действия
Необязательные параметры
Function
Связь с хозяином (Host) - Процедуры глобальные данные параметров Блок взаимодействия
в
качестве
Рекурсия
Процедуры это самостоятельные программные единицы, каждая со своим именем, которое служит для обращения к этим единицам. Структура текста процедуры такова procedure name [(argument list)] [specification statements] [executable statements] ... [CONTAINS internal procedures] END procedure [name]
Где вместо procedure может быть одно из слов SUBROUTINE или FUNCTION.
Имеется некоторое различие типов процедур: •Internal - внутри другой программной единицы •External - внешняя (возможно на языках, отличных от Fortran 90) •Module - размещаемая внутри модуля.
Чтобы использовать процедуры (независимо от типа) требуется оператор вызова. Подпрограммы вызываются оператором CALL, а функции вызываются по имени:
CALL name [( список параметров )]
result = name ( [список параметров] )
В обоих случаях управление передается процедуре из вызывающего оператора и возвращается в тот же оператор по выходе из процедуры. Список параметров пуст или несколько переменных или выражений, значения которых используются процедурой. Заметим, что функция должна включать скобки, заключающие список параметров, даже если список пуст.
Из процедур могут быть созданы их множества. Существуют •встроенные (их в Fortran 90 имеется 113), •библиотеки, напрмер, NAg fl90 Numerical Library has 300+, BLAS, IMSL, LaPACK, Uniras •модули, много свободно распространяемых, см. WWW.
Библиотечные процедуры обычно работают быстрее, даже быстрее чем встроенные! Я
Фактические и формальные параметры
Процедуры могут быть использованы для хорошо определенных задач, использующих доступные им данные. Наиболее общим способом сделать данные доступными процедуре является передача данных в списке параметров при вызове процедуры. Список параметров это просто несколько переменных или выражений (или имен процедур). Параметр(ы) в вызывающем операторе называются фактическими параметрами; параметры в соответствующем операторе процедуры называются формальными. Фактические и формальные параметры соотносятся с их позицией в списке параметров - первый фактический параметр соотносится с первым формальным параметром, второй фактический параметр соотносится со вторым формальным параметром и т.д. Тип данных, ранг и др. у фактических и формальных параметров должно соответствовать точно.
Когда процедура вызвана, исходные данные копируются из фактических параметров в формальные, и копируются обратно из формальных параметров в фактические при возврате. При изменении значения формального параметра процедура может изменить значение фактического параметра. •Пример подпрограммы, которая измененяет значения одного или более своих параметров: REAL, DIMENSION(10) :: a, c ... CALL swap( a,c )
SUBROUTINE swap( a,b ) REAL, DIMENSION(10) :: a, b, temp temp = a a=b b = temp END SUBROUTINE swap
Подпрограмма swap обменивает содержимое двух массивов типа real. (то есть значения одного массива становятся значениями другого массива)
•Пример функции для генерации одного результата, использующего ее параметры (аргументы):
REAL :: y,x,c ... y = line( 3.4,x,c )
FUNCTION line( m,x,const )
REAL :: line REAL :: m, x, const line = m*x + const END FUNCTION line
Функция line вычисляет значение y путем приравнивания напрямую значения функции line. Имя функции line выступает явно как переменная, и оно должно быть определено с тем же типом, что и y и использует память для значения результата функции. Заметим, что в обоих примерах имена формальных параметров могут быть одинаковыми с фактическими параметрами или отличными от имен фактических параметров. Другие правила и особенности оформления формальных и фактических параметров будут рассмотрены ниже в разделе "Параметры процедур". Я Внутренние процедуры
Программные единицы (главная программа, внешние процедуры и модули) могут содержать в себе внутренние процедуры. Внутренние процедуры собираются вместе в конце программного модуля после оператора CONTAINS. Программная единица "хозяйничает" (распоряжается) в любых процедурах, которые содержатся в ней. Внутренние процедуры не могут содержать внутри себя других внутренних процедур и не могут включать оператор CONTAINS.
Внутренние процедуры могут быть только вызванными своим хозяином и другими процедурами, внутренними для того же самого хозяина, не смотря на то, что внутренние процедуры могут вызывать другие процедуры (внешние или модуль). Например:
PROGRAM outer REAL :: a, b, c ... CALL inner( a )
... CONTAINS
SUBROUTINE inner( a ) ! доступна только для outer REAL :: a ! передается как параметр REAL :: b=1.0 ! переопределяется c = a + b ! c соотносится с хозяйской переменной END SUBROUTINE inner
END PROGRAM outer
Программа outer содержит внутреннюю подпрограмму inner. Заметим, что переменные определены у единицы-хозяина и снова переопределены во внутренней подпрограмме. В примере, не смотря на то, что a, b и c определены в outer:
•Значение a передается как параметр, переопределяемая переменная (формальный параметр), также называемой a. Передаваемое значение то же самое, что и программе outer, переменные a в outer и inner это различные объекты
•Подобно a, переменная b переопределена в подпрограмме и также другой объект чем объект b в хозяйской программе. Значение переменной b не передается параметром и не соотносится с хозяином
•c есть единственный объект, общий для outer и inner, соотносится с хозяином.
В связи с правилами переопределения переменных во избежание ошибок полезно определять все переменные, используемые в процедуре.
Я Внешние процедуры
Внешние процедуры содержатся в собственных программных единицах (подпрограммах или функциях), которые могут содержать (то есть иметь во владении) внутренние процедуры. Например:
PROGRAM first REAL :: x x = second() ... END PROGRAM first
FUNCTION second() ! внешняя REAL :: second . . . ! не соотносится с хозяином END FUNCTION second
Внешние процедуры не имеют хозяина и поэтому не могут разделять данные через соотнесение с хозяином. Передача данных с помощью параметров есть наиболее общий способ разделения данных с внешними процедурами. Внешние процедуры можно вызывать из всех других типов процедур. Я Подпрограммы и функции
Подпрограммы (Subroutine)
Рассмотрим следующий пример
PROGRAM Thingy IMPLICIT NONE ..... CALL OutputFigures(Numbers) . . . . .. CONTAINS SUBROUTINE OutputFigures(Numbers) REAL, DIMENSION(:), INTENT(IN) :: Numbers PRINT*, "Здесь фигуры ", Numbers END SUBROUTINE OutputFigures END PROGRAM Thingy
Подпрограмма просто печатает свой аргумент. Внутренние процедуры могут "видеть" все переменные , объявленные в главной программе (и оператор IMPLICIT NONE ). Если внутренняя процедура объявляет переменную, которая имеет то же имя, что и переменная из главной программы, то это объявление определяет новую переменную внутри процедуры. Используя процедуру можно легко заменить формат вывода. Чтобы затем формат действовал для всех выводов, необходимо сделать замену в строке внутри процедуры.
Внутренние процедуры располагаются между операторами CONTAINS и END PROGRAM и имеют следующий синтаксис SUBROUTINE < имя подпрограммы >[ (< формальные аргументы >) ] < объявление формальных аргументов > < объявление локальных объектов > ...
< выполняемые операторы > END [ SUBROUTINE [< имя подпрограммы > ] ]
(Заметим, что не все HPF-компиляторы реализуют внутренние подпрограммы.)
Подпрограмма SUBROUTINE может включать вызов других подпрограмм из той же главной программы, из присоединенных модулей или внешних файлов. Заметим, что как главная программа, так и подпрограмма SUBROUTINE должны заканчиваться оператором END. Полезно в конце подпрограммы SUBROUTINE указывать ее имя. Я Функции Рассмотрим пример, PROGRAM Thingy IMPLICIT NONE ..... PRINT*, F(a,b) ..... CONTAINS REAL FUNCTION F(x,y) REAL, INTENT(IN) :: x,y F = SQRT(x*x + y*y) END FUNCTION F END PROGRAM Thingy
Функции размещены между операторами CONTAINS и END PROGRAM . Они имеют синтаксис:
[< префикс >] FUNCTION < имя функции>( [< форм.арг >]) < объявление форм.арг > < объявление локальных объектов > ... < вычисляемые операторы, присваивание результата > END [ FUNCTION [ < имя функции > ] ]
Допускается объявлять тип функции внутри функции вместо заголовка. Я Переменные в процедуре
Любые переменные, объявленные в процедуре ( любого типа), вызываются как локальные этой процедуре - они не могут быть использованы вне процедуры, в которой они определены (объявлены). Формальные параметры-переменные всегда локальны в процедуре .
Переменные, объявленные внутри процедуры, обычно существуют только во время выполнения процедуры: • Какая бы процедурa ни была вызвана, переменные, объявленные в процедуре , "созданы" и для них выделено требуемое место в памяти • Какая бы процедурa ни существовала, по умолчанию по ее завершении переменные, объявленные в процедуре, "уничтожаются" и любая память, которую процедура имела, будет возвращена снова.
Это "создание" и "уничтожение" переменных процедур означает, что по умолчанию, переменные, объявленные внутри процедуры ,не сохраняют , не помнят значений от одного вызова к другому. Это умолчание может быть преодолено, чтобы позволить локальным переменным помнить их значения от вызова до вызова. Я SAVE
Атрибут SAVE заставляет программу помнить значения переменных процедур от одного вызова до следующего вызова. Любые переменные, которым даны начальные значения при их объявлении, имеют атрибут SAVE по умолчанию. Например:
FUNCTION func1( a_new ) REAL :: func1 REAL :: a_new REAL, SAVE :: a_old ! сохраняемая INTEGER :: counter=0 ! сохраняемая ... a_old = a_new counter = counter+1 END FUNCTION func1
Когда функция вызвана в первый раз, a_old имеет неопределенное значение пока счетчик counter установлен в 0. Эти переменные модифицированы функцией и сохраняемы, так что в любых последовательных вызовах a_old имеет значение предыдущего аргумента и counter это счетчик числа вызовов func1. Я Локальные объекты
В процедуре SUBROUTINE Madras(i,j) INTEGER, INTENT(IN) :: i, j REAL :: a REAL, DIMENSION(i,j):: x
a, и x известны как локальные объекты. Они :
•создаются каждый раз, когда процедура вызывается, •исчезают, когда процедура завершается, •не сохраняются их значения между вызовами, •не используется для них память между вызовами.
x будет вероятно иметь различный размер и образ в каждом вызове. Место обычно выделяется из программного стека. Я Соответствие параметров
Вызов процедуры может быть таким:
CALL OutputFigures(NumberSet)
А ее объявление,
SUBROUTINE OutputFigures(Numbers)
Здесь NumberSet фактический параметр и он соответствует формальному параметру Numbers. В вызове OutputFigures имя Numbers есть иначе называемый NumberSet. Аналогично рассмотрим PRINT*, F(a,b)
и REAL FUNCTION F(x,y)
Фактические параметры a и b соответствуют формальным параметрам x и y. Значение формального параметра заменяется значением фактического. Подробнее см. раздел "Параметры процедур". Я Правила области действия
Fortran 90 не является традиционным языком с блочной структурой: •область действия объекта есть программная единица, где он видим и доступен •внутренние процедуры могут наследовать существующие в главной единице (совокупности). •объекты, объявленные в модулях, могут быть видимы в ассоциации-пользователе (через оператор USE ) -- используется для глобальных данных; Я Связь с хозяином (Host) - глобальные данные
Рассмотрим,
PROGRAM CalculatePay IMPLICIT NONE REAL :: Pay, Tax, Delta INTEGER :: NumberCalcsDone = 0 Pay = ...; Tax = ... ; Delta = ... CALL PrintPay(Pay,Tax)
Tax = NewTax(Tax,Delta) .... CONTAINS SUBROUTINE PrintPay(Pay,Tax) REAL, INTENT(IN) :: Pay, Tax REAL :: TaxPaid TaxPaid = Pay * Tax PRINT*, TaxPaid NumberCalcsDone = NumberCalcsDone + 1 END SUBROUTINE PrintPay
REAL FUNCTION NewTax(Tax,Delta) REAL, INTENT(IN) :: Tax, Delta NewTax = Tax + Delta*Tax NumberCalcsDone = NumberCalcsDone + 1 END FUNCTION NewTax
END PROGRAM CalculatePay
Здесь NumberCalcsDone глобальная переменная. Она доступна во всех процедурах главной ассоциации.
Рассмотрим еще один пример
PROGRAM Proggie
IMPLICIT NONE REAL :: A, B, C CALL sub(A) CONTAINS SUBROUTINE Sub(D) REAL :: D ! D формальный (соответствующий для фактического параметра A) REAL :: C ! локальное C (отлично от С из Proggie) C = A**3 ! A не может быть заменено D = D**3 + C ! B = C ! B из Proggie получает новое значение
D
может
быть
заменено
END SUBROUTINE Sub END PROGRAM Proggie
В процедуре Sub при выполнении с одной стороны A как параметр будет подставлен вместо D, с другой стороны в операторе C = A**3 используется значение А из процедуры Proggie, независимо от того, какой фактический параметр использован при вызове Sub. Есть и другой аспект: А как глобальная величина из процедуры Proggie не может менять значения, но как фактический параметр, соответствующий D, будет перевычислено оператором D = D**3 + C.
C в Sub отдельное от C в Proggie, замена его значения в Sub не приведет к замене значения C в Proggie. Я Блок взаимодействия (Interface Blocks)
Интерфейс происходит всегда, когда одна программная единица вызывает другую. Чтобы работать правильно , программа должна обеспечивать , чтобы фактические параметры в вызове процедуры согласовывались с формальными параметрами этой процедуры. Такой интерфейс проверяется компилятором во время компиляции программы и может быть: •явным - как в вызове внутренних процедур и процедур модуля, где компилятор может видеть подробности вызова параметров процедуры •неявным - как в вызове внешних процедур. Здесь компилятор допускает, что детали вызова процедуры и параметры увязаны.
Как бы ни было интерфейс возможно сделать явным. Это может быть сделано с помощью интерфейсного блока:
INTERFACE Операторы интерфейса END INTERFACE
Интерфейсный блок для процедуры включается в начало вызывающей программной единицы. Операторы интерфейса содержат копию оператора SUBROUTINE (или FUNCTION), все операторы объхявлений для формальных аргумнетов и оператор END SUBROUTINE (или FUNCTION) . Например:
PROGRAM count INTERFACE SUBROUTINE ties(score, nties) REAL :: score(50) INTEGER :: nties END SUBROUTINE ties END INTERFACE
REAL, DIMENSION(50):: data ... CALL ties(data, n) ... END PROGRAM count
SUBROUTINE ties(score, nties) REAL :: score(50) INTEGER :: nties ... END SUBROUTINE ties
Интерфейсный блок в программе count обеспечивает явный интерфейс с подпрограммой ties. Если бы программа count могла бы вызвать другую внешнюю процедуру, ее операторы интерфейса могли бы быть заменены в том же интерфейсном блоке. Я Параметры процедур
Объекты допустимого образа
Одно из мощнейших как в использовании процедур так и представлении задачи сделать так, чтобы однажды написанная и протестированная процедура могла быть использованной и повторно использованой как только потребуется. (даже в другой программе). Поскольку часто имеет место случай, когда программа может хотеть рассматривать массивы или символьные строки различного размера в одной и той же процедуре, Fortran 90 позволяет формальным параметры иметь размер переменных. Такие объекты могут вызывать объекты допустимого образа. Например:
SUBROUTINE sub2(data1, data3, str) REAL, DIMENSION(:) :: data1 INTEGER, DIMENSION(:,:,:) :: data3 CHARACTER(len=*) :: str ...
Формальные параметры data1 и data3 это массивы, которые могут быть объявлены с рангом, но без размера - задан двоеточием ':' , использованным внутри спецификации размера по каждому измерению. Подобным образом, str имеет не явное задание длины и заимствует длину строки фактического параметра. Когда вызывается подпрограмма sub2, все три формальных параметра принимают размер им соответствующих фактических параметров; все три формальных параметра объекты допустимого образа. Я Вид параметра (атрибут INTENT)
Хорошим стилем программирования считается использование указания компилятору, будет параметр входным или выходным с помощью атрибута INTENT •только входным (вызываемым) -- INTENT(IN), означает, что формальный параметр ожидает значение при вызове процедуры, но не изменяется процедурой; •выходным (присваиваемым после использования) -- INTENT(OUT), означает, что формальный параметр не имеет значения при вызове процедуры, но значение будет дано прежде, чем процедура будет завершена; •и входным и выходным -- INTENT(INOUT) означает, что формальный параметр имеет начальное значение при вызове процедуры, и значение будет изменено процедурой.
SUBROUTINE example(arg1,arg2,arg3) REAL, INTENT(IN) :: arg1 INTEGER, INTENT(OUT) :: arg2 CHARACTER, INTENT(INOUT) :: arg3
REAL :: r r = arg1*ICHAR(arg3) arg2 = ANINT(r) arg3 = CHAR(MOD(127,arg2)) END SUBROUTINE example
Другой пример:
SUBROUTINE invert(a, inverse, count) REAL, INTENT(IN) :: a REAL, INTENT(OUT) :: inverse INTEGER, INTENT(INOUT) :: count inverse = 1/a count = count+1 END SUBROUTINE invert
Подпрограмма invert имеет три формальных параметра: а используется в процедуре, но не меняется в ней, имеет атрибут INTENT(IN); inverse вычисляется в процедуре, имеет атрибут INTENT(OUT), и count (счетчик вызовов) увеличивается процедурой и требует атрибута INTENT (INOUT).
Использование атрибута INTENT рекомендуется, чтобы: •позволить хорошему компилятору проверить ошибки кодирования, •выполнить эффективно компиляцию и оптимизацию.
Ключевые параметры
Вместо связывания фактических параметров с формальными только с помощью позиции возможно их связать с помощью имени. Это поможет избежать неприятностей при вызове процедуры часто используется и вызывается несколько встроенных процедур Fortran 90. Например:
SUBROUTINE sub2(a, b, stat) INTEGER, INTENT(IN) :: a, b INTEGER, INTENT(INOUT):: stat ... END SUBROUTINE sub2
Может быть вызвана операторами:
INTEGER :: x=0 ... CALL sub2( a=1, b=2, stat=x ) CALL sub2( 1, stat=x, b=2) CALL sub2( 1, 2, stat=x )
Имена формальных параметров служат ключевыми словами в операторе вызова. Использование ключевых слов не освобождает от следования порядку параметров, заданных без ключевых слов:
CALL sub2( 1, b=2, 0 ) ! неверно CALL sub2( 1, stat=x, 2) ! неверно
Когда используются ключевые слова параметров интерфейс между вызывающей программной единицей и процедурой должен быть явным. Заметим также, что параметры с атрибутом INOUT должны быть ассоциированы с переменной и поэтому значение - stat=0 будет неправильным. Я Необязательные параметры
Иногда не все параметры требуются при каждом использовании процедуры. Потому некоторые аргументы могут быть специфицированы как необязательные с помощью атрибута OPTIONAL:
SUBROUTINE sub1(a, b, c, d) INTEGER, INTENT(INOUT):: a, b REAL, INTENT(IN), OPTIONAL :: c, d ... END SUBROUTINE sub1
Здесь a и b требуются всегда, когда вызывается процедура sub1. Параметры c и d не обязательны и sub1 можно вызывать так:
CALL sub1( a, b ) CALL sub1( a, b, c, d ) CALL sub1( a, b, c )
Заметим, что порядок, в котором появляются аргументы, важен (без использования ключевых слов), так что не возможно вызвать call sub1 с параметром d, но без параметра c. CALL sub1( a, b, d ) ! неверно
Необязательные параметры должны располагаться после всех параметров, связанных с позициями в операторе вызова и требуется явный интерфейс. Возможно проверять, какой или никакой необязательный параметр представлен, когда вызвана процедура с помощью логической встроенной функции PRESENT. Например: REAL :: inverse_c
IF( PRESENT(c) ) THEN inverse_c = 0.0 ELSE inverse_c = 1/c ENDIF
Если необязательный параметр имеется, то PRESENT возвращает значение .TRUE. . В вышеприведенном примере это использовано для ошибки времени выполнения (деление на 0 будет причиной краха программы 'crash'). Я Процедуры в качестве параметров
Возможно использовать как фактический параметр в вызове другой процедуры. Часто получается в результате вызова функции, которая использована как фактический параметр другой процедуры. Например:
PROGRAM test INTERFACE REAL FUNCTION func( x ) REAL, INTENT(IN) ::x END FUNCTION func
END INTERFACE ... CALL sub1( a, b, func(2) ) ... END PROGRAM test
REAL FUNCTION func( x ) ! внешняя REAL, INTENT(IN) :: x func = 1/x END FUNCTION func
Когда вызов sub1 сделан с тремя параметрами a, b и результат функции func(2) - в этом случае возвращаемое значение 1/2. Процедура, которая использована как параметр будет всегда выполняться прежде, чем процедура, в которой оператор вызова появиться. Использование процедуры в качестве параметра требует явного интерфейса.
Заметим, что оператор спецификации для функции func идентифицирует результат типа REAL. Это альтернатива объявлению имени функции как переменной:
REAL FUNCTION func( x ) REAL, INTENT(IN) :: x func = 1/x END FUNCTION func
и
FUNCTION func( x ) REAL :: func REAL, INTENT(IN) :: x func = 1/x END FUNCTION func
эквивалентны. Я Рекурсия
Fortran 90 допускает рекурсивные подпрограммы (подпрограммы , которые вызывают сами себя). В целях оптимизации рекурсивная подпрограмма должна быть специфицирована ключевым словом RECURSIVE . Объявление рекурсивной функции имеет отличие в синтаксисе. Должно быть использовано ключевое слово RESULT , специфицирующее имя переменной, хранящей значение результата функции (Ключевое слово RESULT необходимо, чтобы использовать имя функции для возвращения результата. Массив значений рекурсивной функции допустим и в то же время ссылка на рекурсивную функцию будет неразличима от ссылки на массив. Имя функции неявно имеет тот же атрибут , что и имя результата.) Очевидно, что что рекурсия должна быть объявлена в заголовке:
INTEGER RECURSIVE FUNCTION fact(N) RESULT(N_Fact) или RECURSIVE INTEGER FUNCTION fact(N) RESULT(N_Fact) (Целыми INTEGER будут и fact и N_Fact.)
или, RECURSIVE FUNCTION fact(N) RESULT(N_Fact)
INTEGER N_Fact
В последнем случае INTEGER N_Fact дает тип величине fact; фактически специфицирует тип fact.
Рекурсия может одного из двух типов: •Непрямая рекурсия - A вызывает B вызывает A... •Прямая рекурсия - A вызывает A вызывает A... обе требуют атрибута RECURSIVE для процедуры A. Рекурсивные процедуры требуют осторожного управления. Важно обеспечить ограничения самовызова процедуры. Например, рекурсивная процедура factorial, приведенная выше, использует конструкцию IF , чтобы вызвать себя снова или возвратить зафиксированный результат. Потому здесь есть ограничение на количество вызовов процедуры. Пример рекурсивной функции
RECURSIVE FUNCTION fact( n ) RESULT(N_Fact) INTEGER, INTENT(IN) :: n INTEGER :: N_Fact IF( n==1 ) THEN N_Fact = 1 ELSE N_Fact = n*fact( n-1 ) END IF END FUNCTION fact
Подпрограммы объявляются с помощью заголовка RECURSIVE SUBROUTINE.
Пример рекурсивной подпрограммы
RECURSIVE SUBROUTINE Factorial(N, Result) INTEGER, INTENT(IN) :: N INTEGER, INTENT(INOUT) :: Result IF (N > 0) THEN CALL Factorial(N-1,Result) Result = Result * N ELSE Result = 1 END IF END SUBROUTINE Factorial Родовые подпрограммы
Часто бывает так, что задача представлена процедурой для одного определенного типа данных, но может быть сделана эквивалентная процедура и для другого типа данных. Например, процедура, нужная для сортировки массива вещественных чисел для упорядочения по возрастанию, почти идентична аналогичной процедуре сортировки для целых. Различие толкько в типе данных. 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 Ввод-вывод Интерактивный ввод-вывод Эта глава рассматривает взаимодействие между пользователем и программой с помощью стандартных устройств ввода и вывода (клавиатура и экран). Данные могут запомнены и представлены в нескольких различных видах; данные, запомненные программой в бинарной форме (называемые бесформатными данными), пока программист и пользователь программы предпочтет работать с сивольными (форматными). Когда данные читаются в программу, сиволы конвертируются в машинную бинарную форму. Просто данные, запомненные в бинарной форме, когда пишутся на экран. Просмотр и форматирование данных специфицируются программистом или берется формат по умолчанию, используемый в Fortran 90. Подмножество спецификаций форматирования представлено далее, полное множество is rarely used.
Может использоваться международное шестнадцатиричное представление вещественных чисел:
BE1D7DBF Которое трудно для понимания (ограничено использованием во время записи на экран), но связано с вещественным значением 0.00045. Оно может быть записано по разному
0.45E-03 4.5E-04 0.000450 где E## стоит на месте экспоненты и эквивалентно умножению на 10 в степени x10##.
Конвертация внутреннего представления в читаемую пользователем форму известна как форматированный ввод-вывод и заменяющая внешнюю форму символов ссылками на форматирование. Простой Input и Output Пользователь может присвоить значения переменным, используя оператор READ. Пользователь , желая знать результат работы программы, обычно выводит на экран, используя операто WRITE. Чтобы читать значение в переменную, называемую radius, скажем, следующий оператор будет подходящим. READ(*,*) radius И значение переменной area будет выдано на экран оператором: WRITE(*,*) area Общий вид операторов READ и WRITE : READ( [UNIT=]unit, [FMT=]format ) список переменных WRITE( [UNIT=]unit, [FMT=]format ) список переменных
где unit это целое, ассоциированное с экраном или файлом и format описывает, как данные будут выглядеть. Когда читается с клавиатуры, unit может быть равно 5 или *; когда пишется на экран, unit может быть равно 6 или *: READ(5,*) length, breadth WRITE(UNIT=6,*) temperature, pressure, mass WRITE(*,*) pi*radius**2, 2.0 Некоторые переменные (или выражения) могут быть специфицированы в операторе READ или WRITE. READ(5,*) length, breadth WRITE(6,*) temperature, pressure, mass WRITE(*,*) pi*radius**2, 2.0 Форматирование по умолчанию Когда читается или пишется на экран или с клавиатуры, Fortran-программа автоматически преобразует данные в требуемую форму; символы для экрана, битовые данные для компьютера. INTEGER :: i, j REAL :: data(3) ... READ(*,*) i WRITE(*,*) i, j, data Символ * позволяет программе использовать ввод-вывод по умолчанию. Первая * указывает стандартное устройство, вторая * представляет по умолчанию формат переменных, которые преобразуются из одного типа данных в другой. Эта форма вывода данных быстрая, простая. Форматированный ввод-вывод I/O Оператор FORMAT может быть использован для чтения или записи данных в форме, отличной от формы по умолчанию.Оператор FORMAT это помеченный оператор, указываемый с помощью числовой метки операторами WRITE или READ в том же программном модуле: READ(*,100) i, j WRITE(*,100) i, j READ(*,FMT=200) x, y WRITE(*,200) x, y ...
100 FORMAT (2I8) ! 2I8 дескриптор редактора 200 FORMAT (2F10.6) ! 2F10.6 дескриптор редактора
Форматирование - это знание о том, как надо редактировать ввод-вывод. Ввод-вывод это управляемое использование дескрипторов редактора. Общая форма оператора FORMAT такова: метка FORMAT (список_форматов) где метка - это идентифицирующее число (уникальное в данной части программы), список_форматов - это список дескрипторов редактора, который включает одно или несколько: I, F, E, ES, EN, D, G, L, A, H, T, TL, TR, X, P, BN, BZ, SP, SS, S, /, :, ', и ,(запятая) В данных заметках только следующие дескрипторы будут рассмотрены: I, F, E, ES, EN, A, X, /, :, ', и ,(запятая) поскольку многие дескрипторы редактора включают особые средства, такие как вывод бинарных чисел, и т.п и. имеют ограниченное применение. Помеченный оператор FORMAT может быть заменен указывающим формат списком дескрипторов в виде символьной строки прямо в операторах WRITE или READ, как например: INTEGER :: i, j REAL :: x, y, z ... READ (*,'(2I8)') i, j WRITE (*,'(3F12.6)') x, y, z Это имеет преимущество в лучшей ясности для читателя, которому не нужно смотреть два оператора. Дескрипторы редактора Дескрипторы редактора точно специфицируют, как будут конвертированы в символьную строку для устройства вывода или внутреннего файла I/O или как будут конвертированы из
символьной строки на устройстве ввода или во внутреннем файле. В дескрипторах ниже ключевые буквы имеют следующий смысл: • • • • •
a -число повторений w -ширина поля - общее число символов m -минимальное число цифр d -цифры справа от десятичной точки e -число цифр в экпоненте
Многие дескрипторы редактора могут предшествовать счету повторений и следовать за шириной поля, то есть общим числом цифр. Таким образом , 2I и 3F10.6 могут быть описаны как 2 целых и 3 вещественных числа с плавающей точкой. Ширина поля для целых чисел по умолчанию, 10 для вещественных определяется описанием. В целом, если w больше чем число цифр, требуемых для представления числа, то добавляются ведущие пробелы. Если w слишком мало для представления числа , в выводе печатаются w звездочек и на вводе читаются w левых символов (число таким образом усекается) Оператор I/O будет использоваться и как множество дескрипторов редактора, и элементов списка ввода-вывода. Процесс закончится в следующем дескрипторе редактора, который потребует значения из списка ввода-вывода. Рассмотрим, каковы правила описания представлений для целых величин, вещественных (форма с фиксированным положением точки и в экспотенциальной форме), символьных, логических, как представлять группу пробелов, управлять переходом на строку, страницу и т.д. Я Целые Дескриптор I для управления форматом целых и может иметь форму Iw или Iw.m. Несколько целых могут прочитаны (записаны) в одном и том же формате путем использования счетчика повторений - aIw или aIw.m. Например: INTEGER :: itest=1234567 ! число для записи ... WRITE(*,*) itest ! 1234567 WRITE(*,'(I6)') itest ! ****** WRITE(*,'(I10)') itest ! 1234567 WRITE(*,'(I10.9)') itest ! 001234567
WRITE(*,'(2I7)') itest, 7654321 ! 12345677654321 WRITE(*,'(2I8)') itest, 7654321 ! 1234567 7654321 Дескриптор I10.9 специфицирует всего 10 символов (включая знак минус, если потребуется) с минимальным числом цифр 9, поэтому в выводе появятся дополнительные ведущие нули. Я Вещественные - форма с фиксированной точкой Дескриптор F используется для управления вещественными (или комплексными) числами, когда требуется нотация с фиксированной десятичной точкой. Она имеет форму Fw.d. Несколько вещественных чисел можно читать(писать), используя повторитель. - aFw.d. Например: REAL :: itest=123.4567 ! число для записи ... WRITE(*,*) itest ! 1.2345670E+02 -формат не F WRITE(*,'(F8.0)') itest !
123
WRITE(*,'(F10.4)') itest ! 123.4567 WRITE(*,'(F10.5)') itest ! 123.45670 WRITE(*,'(F10.9)') itest ! ********** WRITE(*,'(2F8.4)') itest, 7654321 ! 123.4567765.4321 WRITE(*,'(2F10.4)') itest, 7654321 ! 123.4567 765.4321 Важно помнить, что десятичная точка помещается в поле вывода ширины w . В верхнем примере, несмотря на то, что они записывали 7 цифр на экран, поле должно быть из 8 символов (или более), чтобы обеспечить место для десятичной точки. Я Вещественные - экспотенциальная форма Дескриптор E используется для управления форматом вещественных (и комплексных) чисел, когда требуется нотация десятичной плавающей точки. Форма Ew.d или Ew.dEe , где e число цифр в экспоненте. Экспонента нужна для показа чисел со значениями ниже 0.001 или выше 1000. Как и прежде несколько чисел могут быть прочитаны (записаны) в одном формате использованием повторителя - aEw.d. Если w слишком велико для представления числа, ведущие пробелы добавляются перед цифрами. Например:
REAL :: itest=123.45*1000000 ! число для записи (кратность- 1 миллион) ... WRITE(*,*) itest !
1.2345670E+02
WRITE(*,'(E10.4)') itest ! 0.1234E+09 WRITE(*,'(E10.5)') itest ! .12345E+09 WRITE(*,'(E10.4E3)') itest ! .1234E+009 WRITE(*,'(E10.9)') itest ! ********** WRITE(*,'(2E12.4)') itest, 7654321 ! 0.12345E+090.76543E+04 WRITE(*,'(2E10.4)') itest, 7654321 ! 0.1234E+090.7654E+04 Допустимы две альтернативные формы дескриптора E:
• •
EN - инженерная - экспонента всегда делима на и значение перед десятичной точкой, лежащее в диапазоне 1..1000 ES - научная - значение перед десятичной точкой всегда лежит в диапазоне 1..10
Обе используются одним и тем же способом как спецификация (дескриптор) Е: REAL :: itest=123.45*100 ! Число записать 100 раз ... WRITE(*,*) itest !
1.2345000E+04
WRITE(*,'(EN13.6)') ! 12.345000E+03 WRITE(*,'(ES13.6)') ! 1.234500E+04 Я Символьные Дескриптор A используется для управления форматом символов и строк в форме A или Aw. Дескриптор A будет писать столько символов, сколько потребуется, чтобы написать (напечатать) строку ширины w. Если w больше чем символьная строка, то добавляются ведущие пробелы перед символам строки. Например: CHARACTER(LEN=8) :: long='Bookshop'
CHARACTER(LEN=1) :: short='B' ... WRITE(*,*) long ! Bookshop WRITE(*,'(A)') long ! Bookshop WRITE(*,'(A8)') long ! Bookshop WRITE(*,'(A5)') long ! Books WRITE(*,'(A10)') long ! Bookshop WRITE(*,'(A)') short ! B WRITE(*,'(2A) short, long ! BBookshop WRITE(*,'(2A3) short, long ! BBoo Когда используется дескриптор A в операторах форматного READ() символьную строку не нужно заключать в кавычки. Я Логические Логические данные форматируются с помощью дескриптора L и имеют форму Lw или aLw для счетчиков повторения. Обычно используются только две формы дескриптора L, L для одиночного символа формат T или F и L7 , который позволяет печатать .TRUE. и .FALSE. . LOGICAL :: ltest=.FALSE. WRITE(*,*) ltest ! F WRITE(*,'(2L1)') ltest, .NOT.ltest ! FT WRITE(*,'(L7)') ltest !
F
Я Заполнение пробелами (пропуск символьных позиций) Дескриптор X используется для ввода пробелов между выводимыми значениями, чтобы обеспечить читаемось, записывается в форме aX. Дополнительные пробелы имеют смысл только при выводе (то есть в операторах WRITE()), они игнорируются в форматных операторах READ(). Например:
INTEGER :: n=1234 ! число для вывода ... WRITE(*,'(I4, 2X, I4)') i, i-1 ! 1234 1233 WRITE(*,'(I4, 4X, I4)') i, i-1 ! 1234 1233 Я Специальные символы Здесь число других символов, которые управляют форматом, более всего используются только в операторах WRITE(). ' ' спецификация для вывода символьной / спецификация перехода на новую строку ( ) групирование дескрипторов, чаще для повторения Например, следующая программа:
INTEGER :: value = 100 INTEGER :: a=101, b=201 ... WRITE(*,'( 'The value is', 2X, I3, ' units.')') value WRITE(*,'( 'a =', 1X, I3, /, 'b = ', 1X, I3)') WRITE(*,'( 'a and b =', 2(1X, I3) )') a, b приведет к выдаче следующих строк на экран:
The value is 100 units. a = 101 b = 201 a and b = 101 201
Заметим, что пропуски могут быть специфицированы дескриптором X или пробелами в строковой строке как в 'b = 201'. Я Namelist Оператор namelist является средством группирования переменных для ввода/вывода. NAMELIST может быть полезен для вывода, для тестирования и отладки программ. Для ввода его использовать труднее, поэтому это не представлено здесь. Оператор NAMELIST использован для определения группы переменных следующим образом: NAMELIST / группа-имен / список-имен-переменных Например: INTEGER :: sat=7, sun=1, mon=2, tues=3, wed=4, thur=5, fri=6 ... NAMELIST / week / mon, tues, wed, thur, fri ! список NAMELIST / week / sat, sun ! может быть продлен Переменные должны быть объявлены прежде, чем упомянут в операторе в группе NAMELIST (и не должно быть смешивания переменных PRIVATE и PUBLIC). Ключевое слово NML= должно быть использовано на месте спецификации формата в операторе ввода/вывода. Например: WRITE (*,NML=week) Будет выведена следующая строка: &WEEK SUN=1, MON=2, TUES=3, .../ Означает вывод аннотированного списка вида: & имя-группы переменная1= значение {, переменная=значение} / Второй формат (включая символы & и / ) должен быть использован только для ввода. Массивы должны быть специфицирваны:
INTEGER, DIMENSION(3) :: items NAMELIST / group / items ITEMS(1) = 1 WRITE (*, NML=group) Будет выдано: &GROUP ITEMS(1)=1 ITEMS(2)=0 ITEMS(3)=0 / Списки ввода-вывода Когда более чем одна переменная пишется или читается в некотором операторе WRITE() или READ(), оператор ссылается на список ввода - вывода. Для вывода могут быть использованы переменные и/или выражения, но для ввода разрешены только переменные. Могут быть использованы неявные циклы DO для любого ввода или вывода, для любых типов данных, в том числе и производныых. Массив может быть специфицирован как задаваемый полностью, или поэлементно, или диапазоном элементов: INTEGER, DIMENSION(10) :: a READ (*,*) a(1), a(2), a(3) ! чтение значений с записью в 3 элемента READ (*,*) a ! чтение 10 значений READ (*,*) a(5:8) ! чтение значений с записью в 4 элемента Элементы массива могут появляться только в списке ввода -вывода для ввода. Например: INTEGER :: b(10), c(3) c= (/1,2,1/) READ (*,*) b(c) ! запрещено Запрещение возникает потому, что b(1) появится дважды. Я Производные типы данных Ввод-вывод в производных типах данных так как если бы компоненты были специфицированы в порядке. Так для p и t типа POINT и TRIANGLE соответственно, где: TYPE point REAL :: x, y END TYPE TYPE (point) :: pt
! TYPE triangle TYPE (point) :: a, b, c END TYPE TYPE (triangle) :: tri следующие две пары опереторов эквивалентны: READ (*,*) pt READ (*,*) pt%x, pt%y ... READ (*,*) tr READ (*,*) tri%a%x, tri%a%y, tri%b%x, tri%b%y, tri%c%x, tri%c%y Объект производного типа данных, который содержит указатель, может появиться в списке ввода-вывода. Ограничение этой проблемы связано с рекурсивными типами данных. Я Неявный цикл DO Список неявного цикла DO похож на сокращенный вариант конструкции циклаDO. Список неявного цикла DO често используется для представления ввода-вывода, где происходит ввод-вывод массива и он имеет общую форму следующую: (object, do_var=start, stop [,step]) где do_var целая переменная (и не может быть указателем). Рассмотрим следующий пример: INTEGER :: j REAL, DIMENSION(5) :: a READ (*,*) ( a(j), j=1,5) ! a(1), a(2), a(3), a(4), a(5) WRITE (*,*) ( a(j), j=5,1,-1) ! a(5), a(4), a(3), a(2), a(1)
Первый оператор будет читать 5 значений, каждое значение присваивается очередному элементу, начиная с первого. Второй оператор будет читать 5 значений , присваивая ихэлементам массива в обратном порядке Список неявного цикла может быть таким: INTEGER :: I, J REAL, DIMENSION(10,10) :: B WRITE (*,*) ((B(I,J),I=1,10), J=1,10) Здесь вид вывода есть альтернатива использованию секции массива. Я Ввод-вывод без продвижения Нормальное действие оператора ввода-вывода состоит в продвижении к следующей записи по завершении оператора. Таким образом на вводе, если запись читалась только частично, остаток вводимой записи сбрасывается. На выводе оператор WRITE() будет завершен на позиции курсора в начале новой строки. I/O без продвижения позволяет запись читать по частям (например, длинная запись неизвестной длины) или создавать искусный пользовательский интерфейс, где приглашение ввода и ответ пользователя появляется в одной и той же строке (но остерегайтесь, что это "аккуратное средство" не определено в стандарте Fortran и зависит от реализации). Имеется комплексное множество правил, превращающих использование ввода-вывода без продвижения и его вариантов в соответствующие ключевые слова. Этот раздел имеет смысл только с весьма зависящим от реализации аспектами управления экраном. Ключевое слово ADVANCE должно быть использовано в операторах WRITE или READ следующим образом: INTEGER :: i, j ... WRITE(*,*,ADVANCE='NO') 'Введите значение i : ' READ(*,*) i WRITE(*,*) ''Введите значение j : ' ! ADVANCE='YES' READ(*,*) j Если пользователь вводит значения 10 и 20 это должно появиься на экране следующим образом:
Введите значение i : 10 Введите значение j : 20 Ввод-вывод без продвижения выглядит ясно, но зависит от реализации. Файловый ввод-вывод В предшествующих модулях весь ввод и вывод представлен работающим с устройством по умолчанию., а именно : с клавиатурой и экраном. Во многих случаях эти действия не подходят: временная память промежуточных результатов, много ввода или вывода, вывод одной программы для ввода в другой, множество вводимых данных используется многократно, и т.д. Требуется механизм, который позволяет программисту обеспечить прямой ввод из другого исходного устройства, но не с клавиатуры (во время выполнения) и представлять вывод в сохраняемой форме. Таким средством является коллекция файлов в памяти . Такие файлы как исходный текст программы или множество данных для ввода.вывода форматированы, то есть представлены упорядоченной последовательностью символов, разделенных маркерами конца или маркером записи. Форматированный файл может быть просмотрен с помощью редактора или напечатан на принтере. Неформатированный файл не имеет такой структуры и может быть рассмотрен как единый поток байтов данных. Неформатированный файл естественно вводить путем использования подходящей программы, написанной пользователем. Номера устройств Оператор доступа к файлам ввода-вывода в Fortran 90 с помощью уникального числового кода или номера устройства. Каждый номера устройства - это целое, которое специфицирует канал данных, который может быть связан с конкретным файлом или реальным устройством. Программа может установить специальным образом эту связь, или использовать умолчание, и может несколько раз прервать и переустановить связь. Это число должно находиться в диапазоне между 1...99. Номер устройства может быть задан так: • • • •
Как целая константа, например 10 Целое выражение, например nunit или nunit+1 звездочкой * , задающей устройство умолчанием имя внутреннего файла Такие операторы как READ(), WRITE() или OPEN() нужно использовать для указания конкретного устройства с ключевым словом UNIT: INTEGER :: nunit=10 ... READ(UNIT=10,*) WRITE(UNIT=nunit,*)
Известные номера устройств зарезервированы для ввода-вывода за клавиатурой и экраном. Номер 5 - за клавиатурой (но она не может быть устройством вывода), номер 6 за экраном (но он не может быть устройством ввода). Следующие операторы READ() все эквивалентны, как операторы WRITE(): READ(*,*) data ! перевызывает ссылкой* установку по умолчанию READ(5,*) data READ(unit=5,*) data ... WRITE(*,*) data ! перевызывает ссылкой* установку по умолчанию WRITE(6,*) data WRITE(UNIT=6,*) data Некоторые компьютерные системы имеют соглашение по именованию, которое номером устройства "map" по умолчанию именует файл. Например, когда используется номер устройства 10 в системе VAX/VMS, оно будет map для файла, называемого FOR010.DAT и у некоторых систем UNIX для файла, называемого fort.10 . Специальная системная информация, такая как эта, обеспечена ссылками на вопросы в руководстве по языку для конкретных систем Операторы READ и WRITE Как было показано раньше оператор READ() имеет вид: READ(clist) список_ввода где clist определен как: [UNIT=] номер устройства, [FMT=] спецификация формата [,REC= номер записи] [,IOSTAT=статус ввода-вывода] [,ADVANCE=продвижение] [,SIZE=целая переменная] [,EOR=метка] [,END=метка] [,ERR=метка] Заметим, что номер устройства и спецификация формата требуются именно в таком порядке (когда ключевое слово не нужно), остальное не обязательно.
Большинство ключевых слов для особых манипуляций с файлом и не будут здесь обсуждаться. Необходимый аргумент для обнаружения ошибок ввода-вывода файла это IOSTAT. Если ошибка возникает во время ввода-вывода, то переменная, указанная для IOSTAT, будет установлена в положительное значение, системно зависящее целое. Значение 0 будет возвращено, если операция полностью успешна. Например: READ (*,*) a,b,c ! читает с клавиатуры, format по умолчанию READ (10,FMT=*) line ! читает с устройства 10, format по умолчанию READ (UNIT=5,*) x,y,z ! читает с клавиатуры READ (UNIT=10,*,IOSTAT=ios) ! ios=0, если все пройдет хорошо. Проблемы с I/O могут быть обнаружены, пока программа выполняется как такая: INTEGER :: fileno=50, ios=0, data ... READ(UNIT=fileno,*,IOSTAT=ios) data ! читает значение из файла IF (ios /= 0) THEN READ(*,*) 'ERROR in reading from file' ! сообщение об ошибке STOP ! завершение программы ENDIF Оператор WRITE Оператор WRITE()имеет общую форму, подобную форме оператора READ(): WRITE(clist) список_вывода где clist определен как: [UNIT=] номер устройства, [FMT=] спецификация формата [,REC= номер записи] [,IOSTAT=статус ввода-вывода] [,ADVANCE=продвижение] [,SIZE=целая переменная] [,EOR=метка] [,ERR=метка] Снова заметим, что номер устройства и спецификация формата требуются именно в том порядке (когда ключевое слово не нужно), остальное не обязательно. Большинство ключевых слов для особых манипуляций с файлом и не будут здесь обсуждаться. IOSTAT остается одним из наиболее необходимых аргументов и работает так же для оператора WRITE() как и для оператора READ(). Например:
INTEGER :: n=10, ios=0 ... WRITE (*,*) a,b,c ! пишет на экран, format по умолчанию WRITE (UNIT=6,*) i,j ! пишет на экран, format по умолчанию WRITE (10,FMT=*) I ! пишет на устройство 10, format по умолчанию WRITE (UNIT=n,FMT=*,IOSTAT=ios) data Оператор OPEN Оператор OPEN() используют для того, чтобы связать номер устройства с файлом и специфицировать свойства того файла, которые отличаются с теми, которые задаются по умолчанию. Оператор можно использовать для создания и связывания с уже существующим файлом. В дополнение к стандартной форме описаны те компиляторы, которые могут обеспечить дополнительные не стандартные ключевые слова. Общая практика программирования помещает все операторы OPEN в подпрограмме, которая вызывается на фазе инициализации главной программы. Оператор OPEN постоянно содержит имена файлов системной спецификации и нестандартные средства таким образом, что программу требуется запускать в более чем одной компьютерной системе, поэтому оператор OPEN должен быть легко локализуем. Оператор OPEN() имеет общий вид такой: OPEN(номер_устройства, [список] ) Где номер_устройства это спецификация номера объявляемого устройства (с или без ключевого слова) и список есть список ключевых слов (рассмотрен ниже). Например, следующие операторы OPEN() открывают файл, ассоциированный с номером устройства 10: INTEGER :: ifile=10 ... OPEN(10) OPEN(UNIT=10) OPEN(UNIT=ifile) Следующие ключевые слова из специфицированных в стандарте языка Fortran 90 и могут специфицировать природу открываемого файла: FILE=filename; где filename это правильная строка конкретной системы. Заметим, что .возможно, например, FILE='output.test' STATUS=st; где st может быть одним из 'OLD', 'NEW', 'REPLACE', 'SCRATCH' или 'UNKNOWN'. Статус 'OLD' специфицирует файл, который уже должет существовать; при статусе 'NEW' создается новый файл; при статусе 'REPLACE' удаляется существующий файл прежде чем будет создан новый (с тем же именем); при статусе 'SCRATCH' создается временный файл, который существует только пока программа выполняется и исчезает по ее завершении. В общем случае используется 'OLD' для ввода и 'NEW' для вывода.
ERR=label; есть метка для оператора GOTO , когда обнаруживается ошибка при открытии файла. Если возможно, используют взамен IOSTAT . IOSTAT=ios; где ios это целая переменная, устанавливается в нуль, если оператор выполняется успешно или в зависящую от реализации константу в противном случае ACTION=act; где act может быть 'READ', 'WRITE' или 'READWRITE', специфицирующие разрешение режимов операций над файлом. По умолчанию зависит от процессора.. Некоторые примеры операторов открытия файла: OPEN (UNIT=10,FILE='fibonacci.out') OPEN (UNIT=11,FILE='fibonacci.out',STATUS='NEW',IOSTAT=ios) IF( ios/=0) THEN WRITE(6,*) 'Error opening file: fibonacci.out.' STOP ENDIF OPEN (UNIT=12, FILE='student.records', STATUS='OLD', & FORM='FORMATTED', IOSTAT=ios) Если Вы несколько не доверяете значениям по умолчанию для некоторых полей оператора OPEN() специфицируйте требуемые значения. Комбинация условий возможных ошибок означает, что точный смысл будет дан спецификацией оператора OPEN() и ассоциирован с регулированием ошибки.. Возможна спецификация некоторых значений аргумента после значений по умолчанию для других потому что некоторые комбинации значений аргументов уникальны. Такие "свойства" не рассматриваются в данном курсе. Оператор CLOSE Этот оператор разрешает прямо прервать связь файла с устройством и может делать это или по завершении программы или так, что связь может быть осуществлена с различными файлами после закрытия файла. В его простейшей форме оператор CLOSE() требует номера устройства (открытого файла), но часто используется также IOSTAT : CLOSE ([UNIT=]unit-number [,IOSTAT=ios]) Например: CLOSE (10) CLOSE (UNIT=10) CLOSE (UNIT=nunit, IOSTAT=ios) Оператор INQUIRЕ Этот оператр может быть использован для проверки статуса файла или свзи с файлом.
Он связывает значения с переменными, специфицированными в списке справки, который указывает статус файла отношением к специфицированному ключевым словам. Оператор INQUIRE() имеет общий вид: INQUIRE (список справки) где список справки может быть любым: FILE=fname или UNIT=unum плюс любое из следующих (существуют другие ключевые слова/параметры, но для более сложно организованных файлов ввода-вывода и не рассматриваемых в этом курсе): [, EXIST=lex] ! true или false [, OPENED=lod] ! true или false [, NUMBER=unum] ! номер устройства [, NAME=fnm] ! имя файла [, FORMATTED=fmt] ! 'YES' или 'NO' [, UNFORMATTED=unfmt] ! 'YES' или 'NO' [, FORM=frm] ! 'FORMATTED' или 'UNFORMATTED' 'EXIST' определяет, существует ли данное имя; 'OPENED' определяет, открыт ли и может ли быть открыт файл текущей программой; 'NUMBER' определяет номер устройства, ассоциированный с файлом; 'NAME' определяет имя, ассоциированное с номером устройства; 'FORMATTED', 'UNFORMATTED' или 'FORM' определят формат файла отчета. Значения, возвращаемые операторами INQUIRE() по параметрам перечислены выше. Память для массивов
Динамический массив Часто всего все переменные могут быть использованы как статические переменные, когда они требуют фиксированной памяти, которая указывается при объявлении переменной. Статический массив объявляется указанием образа (вида) и протяженности, которые не могут меняться, пока программа выполняется. Но иногда, когда программа имеет дело с такими переменными, когда: • •
либо массив оказывается значительно большей размерности, чем затребовано или массиву требуется дать новое значение протяженности, и программу требуется каждый раз перекомпилировать перед ее запуском.
В действительности, динамические (или распределяемые) массивы не определяются образом и изначально не имеют ассоциированной с ними памяти, но память может быть выделена в ходе выполнения программы. Это мощное средство, которое позволяет программу
использовать с таким размером памяти, какого она требует, и без перекомпиляции (выполняется только раз).
Распределяемые массивы Распределяемые массивы определяются их спецификацией, получают фактическое место в памяти с помощью оператора ALLOCATE, возвращают память с помощью оператора DEALLOCATE, в процессе выполнения программы они могут менять свой статус, память для таких массивов берется из общего потока (или кучи). Спецификация Распределяемые массивы декларируются так же как статические. Общий вид таков: type, ALLOCATABLE [,атрибут ] :: name Они должны включать атрибут ALLOCATABLE и ранг массива, но не могут специфицировать протяженность ни в каком измерении, ни образ в целом. Вместо этого используется двоенточие (:) для кажого измерения. Например: INTEGER, DIMENSION(:), ALLOCATABLE :: a ! ранг 1 INTEGER, ALLOCATABLE :: b(:,:) ! ранг 2 REAL, DIMENSION(:), ALLOCATABLE :: c ! ранг 1 По декларации распределяемые массивы не имеют соответствующей памяти и на них нельзя ссылаться, пока не будет явно выделена память. Я Выделение и возвращение памяти Оператор ALLOCATE связывает память с распределяемым массивом. ALLOCATE( имя(границы) [,STAT] ) • •
Если успешно, имя имеет требуемые границы (если действительно STAT=0) Если не успешно, программа завершает выполнение (или будет продолжаться, если действительно STAT>0 ).
Если возможно, будет распределен белее чем один массив с помощью одного оператора ALLOCATE - каждый с различными границами, образом или рангом. Если не специфицирована меньшая граница, она полагается равной 1. Только распределяемый массив с неопределенной для него памятью может быть объектом оператора ALLOCATE: n=10 ALLOCATE( a(100) )
ALLOCATE( b(n,n), c(-10:89) ). Память, использованная распределяемым массивом, может быть возвращена в некоторый момент с помощью оператора DEALLOCATE: DEALLOCATE( имя_массива [,STAT] ) • •
Если успешно, имя_массива не длинный имеет любую связанную память (если действительно STAT=0) Если неуспешно, выполнение останавливается (или будет продолжено со STAT>0, если верно).
Если возможно возвратить более чем один массив с помощью одного и того же оператора DEALLOCATE - каждый массив будет иметь различные границы, образ или ранг. Только распределяемый массив с ассоциированной с ним памятью может быть объектом оператора DEALLOCATE. Следующие операторы возвращают память из предыдущего примера: DEALLOCATE ( a, b ) DEALLOCATE ( c, STAT=test ) IF (test .NE. 0) THEN STOP 'ошибка возвращения' ENDIF Хорошей практикой программирования считается возвращение любой памяти, которая была зарезервирована с помощью оператора ALLOCATE . Остерегайтесь: учтите, что любые данные, сохраненные в возвращенной памяти массива, недоступны! Я Статус распределяемых массивов Распределяемые массивы могут быть в одном из двух статусов (состояний): • •
'распределен' - пока массив имеет ассоциированную с ним память 'не распределен в данный момент' - пока массив не имеет ассоциированной с ним памяти
Статус массива может быть проверен с помощью встроенной логической функции ALLOCATED:
AllOCATED( имя ) которая возвращает значение: • •
.TRUE. если имя имеет ассоциированную память .FALSE. в противном случае
Например: IF( ALLOCATED(x) ) DEALLOCATE( x ) или: IF( .NOT. ALLOCATED( x ) ) ALLOCATE( x(1:10) ) По декларации статус распределяемого массива будет иметь значение 'не распределен в данный момент' и получит значение 'распределен' только после того как успешно будет выполнен оператор ALLOCATE. Как программа продолжится и память, использованная конкретным массивом, будет возвращена, так статус массива вернется к значению 'не распределен в данный момент '. Возможно повторять этот цикл распределения и возвращения памяти массиву (возможно с различным размером и протяженностью каждый раз) любое число раз в одной и той же программе. Я Потоки памяти (куча) Естественно, существуют программы, которые берут память для распределения и возвращения разумно в соответствии со (статическими) переменными. Тем не менее, когда используются динамические массивы, это требует ответственности со стороны программиста. Операторы, подобные ALLOCATE и DEALLOCATE очень мощные. Память, распределяемая с помощью оператора ALLOCATE, может быть получена обратно только с помощью: • •
соответствующего оператора DEALLOCATE завершения программы
Память, распределяемая для локальных переменных (в подпрограмме или функции ), должна быть возвращена до выхода из процедуры. Когда при завершении процедуры удалены из памяти все локальные переменные и программа освобождает любую связанную память для использования чем-то иным. Тем не менее, любая память, связанная с помощью оператора ALLOCATE, будет оставаться "в пользовании" хотя она не имеет связанного имени переменной! Память, связанная, но не доступная более, не может быть освобождена и использована где-нибудь в другом месте в программе и о ней может быть сказано, что она в "неопределенном" состоянии. Такая ситуация приводит к уменьшению общей памяти, доступной для программы, ее называют "утечкой памяти".
SUBROUTINE swap(a, b) REAL, DIMENSION(:) :: a, b REAL, ALLOCATABLE :: work(:) ALLOCATE( work(SIZE(a)) ) work = a a=b b = work DEALLOCATE( work ) ! необходимо END SUBROUTINE swap Автоматические массивы a и b статические переменные - программа выделяет требуемую память, когда вызвана swap и возвращает память по выходу из процедуры. Память, выделенная для распределяемого массива work, должна быть явно возвращена в поток (кучу) памяти. Потоки (куча) памяти совокупные. Повторное использование процедур, которые содержат поток (кучу) памяти, будет увеличивать размер распределенной, но не используемой, памяти. Потоки (куча) памяти могут иметь трудные ощибки соединения, но их можно избегнуть с помощью операций выделить и вернуть память в той же процедуре. Я Что есть указатели? Переменная- указатель или попросту указатель это новый тип переменной, переменнаяуказатель может ссылаться на данные, сохраненные другими переменными (называемыми адресатами), или на область динамически выделяемой памяти. Указатели являются новым средством в стандарте Фортрана и вводят в Fortran 90 строки, подобные языку C. Используя указатели, программист получает: • •
Гибкое средство, альтернативное распределяемым массивам. Инструмент для создания и манипулирования динамическими структурами данных (подобных спискам).
Указатели расширяют средства любого языка. Их использование позволяет программисту реализовывать мощные алгоритмы и реализовать требования к памяти явно в соответствии с размером проблемы. Указатели должны быть специфицированы, для переменных-указателей разрешена операция присваивания, разыменования, проверка статуса указателя (состояния связи с адресатом). Возможно использование динамической памяти и построение списков с помощью ссылокуказателей, включение в списки массивов и данных производных типов, указатели могут быть параметрами процедур, имеется набор функций, оперирующих указателями, рассмотрены ошибки. Я Указатели и адресаты
Указатели это лучшее средство переменных, которое ассоциируется динамически с данными адресата. Указатели говорят "указать на" их адреса и включает проверку адресата: • • •
Переменные одного и того же типа данных как указатели и явно определенны с атрибутом TARGET Другие указатели того же типа данных Динамически выделенную память по указателю
Указатели используют преимущество динамической памяти, но не требуют атрибута ALLOCATABLE. Способность выделять и возвращать память неотъемлемое свойство переменных-указателей. Я Спецификации указателя Общая форма для операторов определения указателя и адресата такая: тип, POINTER [,атрибут] :: список переменных тип, TARGET [,атрибут] :: список переменных Где: • •
Тип есть тип объекта данных, на который может быть указано и может быть как производным типом, так и встроенными типами Атрибут есть список других атрибутов указателя
Указатель должен иметь такие же тип и ранг как его адресат. Для указателя-массива оператор определения должен специфицировать ранг, но не образ (есть границы или протяженность массива). В этом смысле указатели массива подобны динамически выделяемым массивам. Например, следующие три пары операторов все определяют указатели и одно или более переменных, которые могут быть адресатами: REAL, POINTER :: pt1 REAL, TARGET :: a, b, c, d, e INTEGER, TARGET :: a(3), b(6), c(9) INTEGER, DIMENSION(:), POINTER :: pt2 INTEGER, POINTER :: pt3(:,:) INTEGER, TARGET :: b(:,:) Заметим, что следующее есть пример неверного определения указателя: REAL, POINTER, DIMENSION(10) :: pt ! неверно
Атрибут POINTER является несовместимым с атрибутами ALLOCATABLE, EXTERNAL, INTENT, INTRINSIC, PARAMETER и TARGET. Атрибут TARGET несовместим с адресатами EXTERNAL, INTRINSIC, PARAMETER и POINTER. Я Присваивание указателя Здесь два оператора, которые должны действовать на указатели: • •
Оператор присваивания указателя (=>) Оператор присваивания (=)
Чтобы связать указатель с адресатом используют оператор присваивания указателя (=>): pointer => target Где pointer есть переменная указателя и target любой правильный адресат. Теперь pointer должна быть использована в качестве вымышленного имени данного, хранимого адресатом target. Итак оператор присваивания указателя выделяет память, требуемую указателем. Чтобы заменить значение адресата указателя (подобно значению переменной) используют обычный оператор присваивания (=). Как присваивание других переменных с указателем, используемых в качестве вымышленного имени другой переменной.. Ниже примеры оператора присваивания: INTEGER, POINTER :: pt INTEGER, TARGET :: x=34, y=0 ... pt => x ! pt указывает на x y = pt ! y эквивалентно x pt => y ! pt указывает y pt = 17 ! y эквивалентно 17 Эти операторы объявления специфицируют три переменных: pt указатель целого; x и y допустимые адресаты указателя. Первый выполняемый оператор связывает адресат х с pt. Второй выполняемый оператор заменяет значение переменной y на значение адресата указателя pt - только оно будет выделено, когда pt свяжется с адресатом. Третий выполняемый оператор меняет связь указателя с другим адресатом. Наконец, четвертый выполняемый оператор связывает новое значение 17 с адресатом pt (но не с самим pt !). Результат вышеприведенных операторов иллюстрируется ниже:
Возможно связать адресат с указателем, используя другой указатель. Например: REAL, POINTER :: pt1, pt2 ... pt2 => pt1 ! верно только если pt1 имеет связанный адресат Не смотря на это может появиться указатель, указывающий на другой указатель, pt2 не указывающий на pt1 самого, но на адресат указателя pt1. Неправильно думать об 'оковах указателя', одного указателя на другой. Вместо этого все такие указатели связываются с одним и тем же адресатом. Остерегайтесь использования следующих операторов, они оба верны: pt1 => 17 ! константное выражение - не верный адресат pt2 => pt1 + 3 ! арифметическое выражение - не верный адресат Я Разыменование (Dereferencing) Когда указатель использован как вымышленное имя для переменной, он автоматически разыменовывается и значение адресата используется быстрее чем сам указатель. Для того, чтобы разыменовать указатель эти способом, требуется , чтобы он был связан с адресатом. Указатель автоматически разыменовывается, когда он появляется: • •
Как часть выражения В операторе I/O
Например:
pt => a b = pt ! b устанавливается равным значению a - pt разыменовано IF( pt<0 ) pt=0 ! pt разыменовано всегда WRITE(6,*) pt ! адресат указателя pt записан READ(5,*) pt ! значение сохранено у адресата указателя pt Я Статус (состояние) связи указателя Указатель может быть в одном из трех состояний: • • •
связан - когда указывает на на правильный адресат откреплен - результат оператора NULLIFY неопределен - начальное состояние после определения
Указатель может получить " откреплен " в результате оператора NULLIFY: NULLIFY( список указателей ) Указатель, который может быть анулирован, может с помощью указания "ничто". Статус указателя можно узнать с помощью встроенной функции: ASSOCIATED ( список указателей [,TARGET] ) Значение, возвращаемое с помощью ASSOCIATED есть .TRUE. или .FALSE. . Когда TARGET отсутствует, ASSOCIATED возвращает значение .TRUE., если указатель связан с адресатом, и .FALSE., если указатель анулирован. Когда задан TARGET, ASSOCIATED сообщает, указывает ли указатель на адресат в вопросе. ASSOCIATED возвращает значение .TRUE. , если указатель связан с TARGET, и .FALSE. , если указатель указывает на другой адресат или может быть анулирован. Это есть ошибка тестирования статуса неопределенного указателя. Рекомендуется обнулять все указатели, которые не связаны непосредственно с адресатом после определения. Следующий пример показывает использование функции ASSOCIATED и оператора NULLIFY: REAL, POINTER :: pt1, pt2 ! неопределенный статус REAL, TARGET :; t1, t2 LOGICAL :: test pt1 => t1 ! pt1 связан pt2 => t2 ! pt2 связан test = ASSOCIATED( pt1 ) ! .T. test = ASSOCIATED( pt2 ) ! .T.
... NULLIFY( pt1 ) ! pt1 связь анулирована test = ASSOCIATED( pt1 ) ! .F. test = ASSOCIATED( pt1, pt2 ) ! .F. test = ASSOCIATED( pt2, TARGET=t2) ! .T. test = ASSOCIATED( pt2, TARGET=t1) ! .F. NULLIFY( pt1, pt2) ! связь анулирована Неопределенный начальный статус указателей изменен на связанный оператором присваивания указателя. Затем функция ASSOCIATED возвращает значени .TRUE. для обоих указателей. Указатель pt1 анулирован и его статус снова проверяется. Статус связи для pt2 относительно адресата снова проверяется. Наконец, оба указателя анулируются в одном операторе.. Я Динамическая память Как указание на существующие переменные, которые имеют атрибут TARGET , так и указатели могут быть связаны с блоками динамической памяти. Эта память связывается с помощью оператора ALLOCATE, который создает неименованную переменную или массив указанного размера, и с типом данных, рангом и т.п. указателя: REAL, POINTER :: p, pa(:) INTEGER :: n=100 ... ALLOCATE( p, pa(n) ) ... DEALLOCATE( p, pa ) В приведенном примере p указывает на область динамической памяти и может владеть одним числом типа real , а pa указывет на блок динамической памяти из 100 чисел типа real. Когда память боль не нужна, она может быть возвращена (освобождена) с помощью оператора DEALLOCATE . В этом отношении указатели имеют много похожего с выделяемыми массивами. Я Общие ошибки Выделение памяти указателям может обеспечивать большую степень свободы программировании. тем не менее следует быть осторожным в программировании: •
в
память становится известной и возникать из выделения динамической памяти указателю и когда переприсваивается указатель другому адресату:
INTEGER, POINTER :: pt(:) ... ALLOCATE( pt(25) )
NULLIFY( pt ) ! неправильно Поскольку указатель есть только способ ссылки на выделенную память (то есть выделенная память не имеет связанного имени переменной, отличной от указателя), переприсваивание указателя означает связанную с ним память, которая не может быть реализована более длинной. Затем вся связанная память будет освобождена прежде, чем модифицируется указатель на нее.. •
Возможно присваивать указателю адресат, но когда перемещается адресат (при освобождении памяти или выходе процедуры , к нему локальной), в этом случае указатель может быть слева "болтающимся":
REAL, POINTER :: p1, p2 ... ALLOCATE( p1 ) p2 => p1 DEALLOCATE( p1 ) ! неверно В приведенном примере p2 указывает на память, связанную с p1. Не смотря на это когда память освобождена p2 no longer имеет верный адресат и получает свой статус неопределенным. В этом случае разыменование p2 приведет к непредсказуемому результату. Ошибки программирования, подобные вышеприведенным, можно избежать безусловно с помощью заготовки, которая все указатели на умершие адресаты анулирует. Я Array Pointers Указатели могут вести себя как динамические псевдонимы для массивов и секций массивов. Такие указатели называют указателями массива. Указатели массива можно использовать, когда секция ссылается часто и может сохранять копию данных. Например: REAL, TARGET :: grid(10,10) REAL, POINTER :: centre(:,:), row(:) ... centre => grid(4:7,4:7) row => grid(9,:)
Указатель массива может быть связан со всем массивом или выбранной частью. Размер и протяженность массива можно изменить так, как требуется даже с рапределяемыми массивами. Например: centre => grid(5:5,5:6) ! внутренние 4 элемента от прежней середины Заметим, что указатель массива не нужно возвращать прежде чем переопределены его протяженность или границы. INTEGER, TARGET :: list(-5:5) INTEGER, POINTER :: pt(:) INTEGER, DIMENSION(3) :: v = (/-1,4,-2/) ... pt => list ! указывает границы для pt pt => list(:) ! указывает границы для pt pt => list(1:5:2) pt => list( v ) ! неверно
Протяженность (или границы) секции массива определяются типом присваивания, использованным для присваивания указателя. Когда указатель массива определяется массивом, соответствующий указатель массива принимает вид его расширения к массиву-
адресату как это было выше с pt => list , оба имеют границы -5:5. Если указатель массива связывается с секцией массива (даже если секция равна всему массив у), его меньшая граница в каждом измерении равна 1 - как с pt => list(:) выше, протяженость pt равна 1:11, тогда как протяженность для list равна -5:5. Так pt(1) привязан к list(-5), pt(2) к list(-4), и т.д. Возможно связать указатель массива с секцией массива, определенной с помощью тройки выписки. Невозможно связать один с секцией массива , определенной выпиской вектора , выше v . Присваивание уазателя pt => list(1:5:2) верно с pt(1), связанным с list(1), pt(2), связанным с list(3) и pt(3) ), связанным с list(5). Я Указатели в производных типах данных Указатели могут быть компонентами производных типов данных. Они могут занимать место распределяемых массивов с производными типами данных или действовать как указатели к другим объектам (включая другие производные типы данных). Динамическая природа указателей массивов может предусматривать переменный объем памяти для производного типа данных: TYPE data REAL, POINTER :: a(:) END TYPE data TYPE( data ) :: event(3) DO i=1,3 READ(5,*) n ! n меняется в цикле ALLOCATE( event(i)%a(n) ) READ(5,*) event(i)%a END DO Число значений различно для каждого event - размер указателя массива зависит от введенного значения n. Когда данные более не требуются , указатель массива будет возвращен: DO i=1,3 DEALLOCATE( event(i)%a ) END DO Я Связанные списки Указатели могут указывать на другие члены того же типа, в этом случае создаются 'cвязанные списки'. Например, рассмотрим следующий тип данных: TYPE node REAL :: item
TYPE( node ), POINTER :: next END TYPE node Производный тип node содержит единственный объект item (данное в списке) и указатель next к другому экземпляру типа node. Заметим, что рекурсивная техника в определении позволяет указателю ссылаться на свой собственный тип данных.
Связанные списки очень мощная программистская концепсия: ее динамический характер означает, что они могут расти или сжиматься как требуется. Требуется осторожность, чтобы гарантировать указателям установки и сохранение корректно - последний указатель в списке обычно нулевой.
ris Я Параметры - указатели Подобно другим типам данных указатели могут рассматриваться как параметры процедур. Тем не менее некоторые аспекты вспомним, когда указатели используются как фактические или формальные параметры: •
•
Как и другие переменные, фактические и формальные параметры должны иметь одинаковые тип и ранг. Формальные параметры - указатели не могут иметь атрибут INTENT, поскольку не ясно, будет ли вид вызван к указателю сам или связан с адресатом. Параметры-указатели для внешних процедур требуют наличия блока INTERFACE.
Когда и фактический и формальный параметры являются указателями, адресат (если он один) и связанный статус рассматривается в вызове и снова при возврате. Важно обеспечить, чтобы адресат оставался верным, когда происходит возвращение из процедуры (то есть адресат не локальная переменная процедуры), в противном случае указатель является левым "висячим" ('dangling'). Когда фактический параметр является указателем и соответствующий формальный параметр таковым не является, указатель разыменовывается и адресат копируется как формальный параметр. При возврате адресат берет значение формального параметра. Поэтому требуется, чтобы фактический параметр был связан с адресатом, когда вызывается процедура.
Например: PROGRAM prog INTERFACE ! нужно для внешних subroutine SUBROUTINE suba( a ) REAL, POINTER :: a(:) END SUBROUTINE suba END INTERFACE REAL, POINTER :: pt(:) REAL, TARGET :: data(100) ... pt => data CALL suba( pt ) CALL subb( pt ) ... CONTAINS SUBROUTINE subb( b ) ! internal REAL, DIMENSION(:) :: b ! допустим образ из 100 ... END SUBROUTINE subb END PROGRAM prog SUBROUTINE suba( a ) ! внешняя subroutine REAL, POINTER :: a(:) ! указывает на данные ... END SUBROUTINE suba Возможно для фактических параметров, не указателей, быть связанными с формальными параметрами - указателями. Я Функции для указателей Функции могут возвращать указатели в качестве своего результата. Они должны быть использованы, когда размер результата зависит от вычислений функции. Заметим, что: • • •
Результат должен иметь атрибут POINTER Возвращающая функция должна иметь правильный адрес или быть нулевой Результат в виде указателя для внешней процедуры требует наличия блока INTERFACE
Например: INTERFACE FUNCTION max_row ( a ) REAl, TARGET :: a(:,:) REAL, POINTER :: max_row(:) END FUNCTION max_row
END INTERFACE REAL, TARGET :: a(3,3) REAL, POINTER :: p(:) ... p => max_row ( a ) ... FUNCTION max_row ( a ) ! внешняя REAL, TARGET :: a(:,:) REAL, POINTER :: max_row(:) ! результат функции INTEGER :: location(2) location = MAXLOC( a ) ! ряд и колонка для максимального значения max_row => a(location(1),:) ! указатель на max row END FUNCTION max_row Здесь внешняя функция max_row возвращает ряд матрицы, содержащий наибольшее значение. Исход указателя только разрешает указать на формальный параметр, поскольку он объявлен как адресат, в противном случае будет иметь локальный массив и слева указатель повиснет при возврате. Обратим внимание на результат функции, использованной в правой части оператора присваивания указателя. Результат указателя может быть использован в выражении, в котором он должен быть ассоциирован с адресом. В разделе приведены примеры и результаты их запуска на Alfa № п/п
Тема
Использованные операторы и атрибуты
1
Взаимодействие подпрограмм через параметры и COMMONблоки
PROGRAM, CONTINUE, CALL, PRINT , FORMAT , STOP, DO, SUBROUTINE , COMMON, END, DIMENSION, EQUIVALENCE, RETURN, FUNCTION
2
Декларации данных, объектноориентированные и атрибутные, указатели и адресаты
Subroutine,!, DATA, integer, real, character, complex, write, end, DO, END DO, DIMENSION, POINTER, TARGET, PARAMETER, logical, write, associated, interface, end interface, call
3
Внутренние подпрограммы, структурный тип
integer, real, type, end type, format, write, call, contains, subroutine, intent, end subroutine, end program
4
Определение и использование модуля, родовые процедуры
Module, end module, contains, real, integer, function, end function, use, module procedure, interface, end interface, write, program, end program
5
Приемы
INTERFACE,
программирования
END
INTERFACE,
обработки массивов подпрограммами
с
SUBROUTINE, END SUBROUTINE, INTEGER, REAL, INTENT, END, OPTIONAL, character, DIMENSION, do,end do, write, do while, read,CALL, if.. then.. else.. end if, present, FUNCTION, RETURN
6
Глобальные данные
PROGRAM, IMPLICIT NONE, REAL, INTEGER, CALL, Write, CONTAINS, SUBROUTINE, INTENT, PRINT, END SUBROUTINE, REAL FUNCTION, END FUNCTION, END PROGRAM
7
Разреженная матрица
Program, TYPE, END TYPE, REAL, INTEGER, TYPE (NONZERO), do, end do, write, end
8
Родовые процедуры
Module, end module, interface, end interface, function, result, end function, real, double precision, program, use, data, write, end program
9
Условная обработка массива
DO, END DO, WHERE
10
Выделение и возвращение памяти для массивов
ALLOCATABLE, ALLOCATE, DO, END DO, IF, ALLOCATE D, DEALLOCATE
Пример 1. Взаимодействие подпрограмм через параметры и COMMON-блоки В файле /comm/f90exampl$ ./fcomm1.f программа, показывающая взаимодействие программ через параметры, блоки COMMON и оператор EQUIVALENCE.
PROGRAM MAIN COMMON B(50,50) , U(50) N= 20 DO 1 I=1,N DO 1 J=1,N B(I,J)=I+J
1 CONTINUE DO 20 i= 1,N 20 U(i)=1 CALL MATRIX (N) C DO 30 k=1,5 print 31, b(1,k) 30 continue 31 format (' A=', F10.2) Z = SKALAR (N) PRINT 3 , Z 3 FORMAT (' Z=',F15.5 ) STOP END c SUBROUTINE MATRIX (N) COMMON A(50,50) , X(50) DIMENSION Y(50) EQUIVALENCE (A(1),Y(1)) DO 2 I =1,N S= 0.0 DO 1 J = 1,N 1 S = S +A(I,J)*X(J) 2 Y(I) = S
RETURN END FUNCTION SKALAR (N) COMMON Y(50) , F(49,49) , X(50) S = 0.0 DO 1 I =1,N 1 S = S+X(I)*Y(I) SKALAR = S RETURN END
В результате трансляции получен абсолютный код в файле /comm/f90exampl$ ./fcomm1
Вот что видно на экране после запуска al2:~/ comm/f90exampl$ ./fcomm1 A= 230.00 A= 3.00 A= 4.00 A= 5.00 A= 6.00 Z= 0.00000 Press any key to continue... Пример 2. Декларации данных, объектно-ориентированные и атрибутные В примере вызываются три подпрограммы exdata, entidec и attrdec. В подпрограмме exdata даются примеры с операторами DATA. В подпрограмме entidec демонстрируются объектно-
ориентированные декларации, содержащие описание свойств данных, указатели и адресаты для них, операция связи указателя с адресатом. В подпрограмме attrdec демонстрируются описание интерфейса в качестве предварительного описания вызываемой подпрограммы, атрибутное описание данных и еще раз указатели и адресаты. program example2 call exdata ! пример с операторами DATA call entidec ! пример с объектно-ориентированными объявлениями call attrdec ! пример с aтрибутными объявлениями end subroutine entidec ! Entity-oriented declaration: INTEGER, DIMENSION (:), POINTER :: days, hours ! Указатели на массив с неопределенными границами INTEGER (2), PARAMETER :: limit=12 INTEGER, TARGET :: A(4), B(8) ! Aдресаты для указателей INTEGER , POINTER :: d, h ! Указатели на целые INTEGER (2) :: k = 4 INTEGER, TARGET :: p ! Aдресат для указателя Logical ans days => A ! Cвязь указателя с массивом DO I = 1 , 4 A(I) = I B(2 * I - 1) = 2 B(2 * I) = 6 + I END DO
Ans=associated(days) ! Cведения о наличии связи указателя с массивом Write(*,'((a5),l1)') ' ans=',ans P=5 d => p ! Cвязь указателя с переменной write (*,'((a4),2i2)') ' p =', d, p end subroutine attrdec interface ! Начало описания интерфейса subroutine summaa(poa,n,s) integer, DIMENSION(:), POINTER :: poa integer n, s end subroutine summaa end interface !Attribute-oriented declaration: INTEGER days, hours, t INTEGER (2) :: k=4,limit DIMENSION days(:), hours(:) POINTER days, hours PARAMETER ( limit=12) INTEGER, TARGET :: A(4), B(8) DO I = 1 , 4 A(I) = I B(2*I-1) = 2
B(2*I) = 6 + I END DO Days => A write (*, '((a3) ,i2)' ) ' k=', k call summaa(days, 4, t) write (*, '((a3), i5 )' ) ' t =', t days => A hours => B DO i = 1, 4 Write (*, '( (a3), i2, (a3), i3)' ) ' a(', i, ')=' , days(i) END DO End Subroutine summaa ( poa, n, s) integer, DIMENSION(:), POINTER :: poa integer n, s s=0 do i = 1, n write (*, '( (a13), i2, (a3), i3)' ) 'with poa a(', i, ')=' , poa(i) s = s + poa (i) end do write (*, '( (a3), i2)' ) 's=' , s end Subroutine exdata
! statement DATA integer n, order, alpha, list(100) real coef(4), eps(2), pi(5), x(5,5) character*12 help complex*8 cstuff DATA n /0/, order /3/ DATA alpha /'A'/ DATA coef /1.0, 2*3.0, 1.0/, eps(1) /.00001/ DATA cstuff /(-1.0, -1.0)/ ! The following example initializes diagonal and below in ! a 5x5 matrix: DATA ((x (j, i), i = 1, j ), j = 1, 5) / 15 * 1.0 / DATA pi /5*3.14159/ DATA list / 100 * 0 / DATA help(1:4), help(5:8), help(9:12) /3*'HELP'/ Write (*, '((a3), i2)' ) ' n=' , n Write (*, '((a7), i2)' ) ' order=', order End Результат запуска получился такой n= 0 order= 3 ans=T p=55
k= 4 with poa a( 1 )= 1 with poa a( 2 )= 2 with poa a( 3 )= 3 with poa a( 4 )= 4 s=10 t =10 a( 1 )= 1 a( 2 )= 2 a( 3 )= 3 a( 4 )= 4 Press any key to continue...
Пример 3. Внутренние подпрограммы, структурный тип В примере демонстрируется описание и использование структурного типа dt, описание внутренней подпрограммы averag, ее параметрам задаются виды in, out, inout. А также показывается трансяция с помощью .bat -файла. Текст программы !calculate value into a running average and retutn the average cubed type dt integer count real average end type type (dt) r 10 format (1x, i5,1x,f5.2,2x,f5.1,2x,f3.1) r%count=5 r%average=4 val =5 cu_ave=1 write(*,10) r%count, r%average, val , cu_ave call averag (val , r, cu_ave)
write(*,10) r%count, r%average, val , cu_ave Contains subroutine averag (value, data1, cube_ave) type (dt) :: data1 real dummy ! значение не может быть изменено, пока cube_ave не будет определено ! прежде, чем будет использовано. Data1 определено, когда процедура ! вызвана и получит переопределенное в подпрограмме. intent (IN) :: value; intent (OUT) :: cube_ave intent (INOUT) :: data1 ! count number of times AVERANGE has been called jn the data set ! being passed. dummy = count*average+value data1%count=data1%count+1 data1%average=dummy/data1%count cube_ave=data1%average**3 end subroutine averag end program Программу запускаю на трансляцию командой al2:~/ comm/f90exampl$ ff90.bat f90_ex4 Результат трансляции таков al2:~/ comm/f90exampl$ ff90.bat f90_ex4 Press any key to continue... В файле ff90.bat находится команда f90 -O0 -o $1 $1.f -lm Запускаю командой al2:~/ comm/f90exampl$ f90_ex4 Результат запуска 5 4.00 5.0 1.0 6 0.83 5.0 0.6 Press any key to continue...
Пример 4. Определение и использование модуля, родовые процедуры
Пример демонстрирует использование модуля. Заметим, что определение модуля должно предшествовать его использованию. В модуле дано предварительное объявление свойств процедур sub1 и sub2, их текст дан уже вне модуля как описание процедур модуля. Программа change_kind использует объявленный модуль. В ней подпрограммы sub1 и sub2 объединены в родовое семейство default, которое используется для вычисления вещественной или целой величины, применяются преобразователи типа - функции real и int. Выбор нужной подпрограммы осуществляется в соответствии с описанием интерфейса и типа аргумента. ! procedure sub1 and sub2 defined as follows: module Module1 contains function Sub1(y) real(8) y sub1=real(y) end function function Sub2(z) integer(2) z sub2=int(z) end function end module !A hpogram that changes non-default integers and reals ! into default integer and reals program change_kind use Module1 interface default module procedure Sub1, Sub2 end interface ! integer(2) in integer indef real(8) re in=5 indef=default(in) write (*,'(i4)') indef re=3.5 redef=default(re) write (*,'(f4.2) ') redef end program Трансляция прошла успешно и при запуске получен результат: al2:~/ comm/f90exampl$ ./f90_ex3 5 3.50 Press any key to continue...
Пример 5. Приемы программирования обработки массивов с подпрограммами Пример показывает приемы программирования: работа с массивом, цикл, описание и вызов подпрограмм, интерфейс к процедурам (при ключевых и необязательных параметрах нужно обязательно задавать интерфейс), типы всех параметров, формальных и фактических, должны соответствовать, свойство быть входным или выходным параметром должно правильно использоваться и др. Показано, что если параметр INOUT задан через ключевой параметр с заданием исходного значения, то вычисленное выходное значение не возвращается !!! Показано действие функции present() для необязательных параметров. Показаны операторы write с выдачей на экран и оператор read c вводом с клавиатуры. ! test array, loop, interface, key argument,input/output,intent,optional ! massiv - loop do -- end do INTERFACE SUBROUTINE sub1(a, b, c, d) INTEGER, INTENT(INOUT) :: a, b REAL, INTENT(IN), OPTIONAL :: c, d END SUBROUTINE sub1 ! SUBROUTINE sub2(a,b,stat) INTEGER, INTENT(IN) :: a, b INTEGER, INTENT(INOUT) :: stat END SUBROUTINE sub2 END INTERFACE ! character(1) input integer k, l, a, b, stat
real p, q DIMENSION array(20) INTEGER :: x = 0 do j = 2, 20, 2 array(j) = 12.0 end do ! ! perfom a function 11 times c do k=-330, -60, -3 int = j / 3 isb = -9 -k array(isb) = MyFunc(int) end do C do j = 1, 10 write (*, '(i5)' ) j end do write (*, '(i5)' ) j ! loop do while -- end do input = ' ' do while ((input .ne. 'n') .and. (input .ne. 'y')) write (*, '(A)' ) 'Enter y or n ' read (*, '(A)' ) input
end do ! k=1 ; l= 2 write (*,'(a6, i3, a3 ,i3 )' ) ' 1. a=', k, ' b=', l CALL sub1( k, l ) k=2; l= -2; p=3.2 ; q=-3.6 CALL sub1( k, l, p, q ) write (*,'(a6,i3,a3,i3 )') ' 2 . a=',k,' b=',l k=3; l=4; p=5 CALL sub1( k, l, p ) write (*, '(a6, i3, a3, i3 )' ) ' 3 . a=', k, ' b=', l ! CALL sub2( a = 1, b = 2, stat = x ) write (*, '(a9,i3)' ) ' 1. stat=', stat CALL sub2( 1, stat = x, b=2) write (*, '(a9, i3)' ) ' 2. stat=',stat CALL sub2( 1, 2, stat = x ) write (*, '(a9, i3)' ) ' 3. stat=', stat stat = 0 CALL sub2( 1, 2, stat ) write (*,'(a9,i3)') ' 4 . stat=',stat END SUBROUTINE sub1( a, b, c,d ) INTEGER, INTENT(INOUT) :: a, b
REAL, INTENT(IN), OPTIONAL :: c, d write (*,'(a13,l5)') ' present(c)=', present(c) write (*,'(a13,l5)') ' present(d )=', present(d) if (present(c) .and. present(d)) then ! Function present(d) - задан ли параметр a=a+b+c+d b=c-d else a=a+b b=a-b end if END SUBROUTINE sub1 FUNCTION MyFunc (i) integer i write (*, '(I5)' ) i myfunc = i RETURN END SUBROUTINE sub2(a, b, stat) INTEGER, INTENT (IN) :: a, b INTEGER, INTENT(INOUT) :: stat Stat = a + b + stat write (*, '(a13 , i3)' ) ' sub2: stat=', stat END SUBROUTINE sub2
В результате запуска получаем al2:~/ comm/f90exampl$ ./f90_ex5 1 2 3 4 5 6 7 8 9 10 11 Enter y or n y 1. a= 1 b= 2 present(c)= F present(d )= F present(c)= T present(d )= T 2 . a 0 b= 6 present(c)= T present(d )= F
3 . a 7 b= 3 sub2: stat= 3 1. stat= 0 sub2: stat= 3 2. stat= 0 sub2: stat= 3 3. stat= 0 sub2: stat= 3 4 . stat 3 Press any key to continue... Пример 6. Глобальные данные Пример показывает NumberCalcsDone)
описание
и
использование
! global date PROGRAM CalculatePay IMPLICIT NONE REAL :: Pay, Tax, Delta INTEGER :: NumberCalcsDone = 0 Pay = 2.5; Tax = -3.567 ; Delta = 234.567 CALL PrintPay (Pay, Tax) Tax = NewTax (Tax, Delta) write (*,'(a7,i2)') ' count=',NumberCalcsDone CONTAINS SUBROUTINE PrintPay (Pay, Tax) REAL, INTENT (IN) :: Pay, Tax
глобальных
данных
(переменной
REAL :: TaxPaid TaxPaid = Pay * Tax PRINT *, TaxPaid NumberCalcsDone = NumberCalcsDone + 1 END SUBROUTINE PrintPay REAL FUNCTION NewTax (Tax, Delta) REAL, INTENT (IN) :: Tax, Delta NewTax = Tax + Delta * Tax NumberCalcsDone = NumberCalcsDone + 1 END FUNCTION NewTax END PROGRAM CalculatePay Здесь переменная NumberCalcsDone - глобальная для головной подпрограммы PrintPay и для подпрограммы-функции NewTax.
программы,
для
В результате запуска получаем al2:~/ comm/f90exampl$ ./f90_ex6 -8.917500 count= 2 Press any key to continue... Пример 7. Разреженная матрица Пример показывает работу со структурой (разреженная матрица) : задание значений через обращение к функции с именем, совпадающим с именем типа, определенного в описании. program razrmatr TYPE NONZERO ! определение типа из трех компонент REAL VALUE INTEGER ROW, COLUMN
END TYPE TYPE (NONZERO) :: A(100) ! описание с новым типом A(1) = NONZERO(5.0, 1, 1) ! задание значения элементу из первой строки и первой колонки A(2) = NONZERO(1.20, 2, 2) A(3) = NONZERO(1.45, 3, 3) A(4) = NONZERO(5.2, 4, 4) A(5) = NONZERO(5.2, 5, 5) A(6) = NONZERO(3.4, 6, 6) A(7) = NONZERO(15.4, 7,7) A(8) = NONZERO(1.0, 8, 8) A(9) = NONZERO(5.7, 9, 9) A(10) = NONZERO(1.8, 10, 10) A(11) = NONZERO(5.2, 11, 11) A(12) = NONZERO(17.2, 12, 12) A(13) = NONZERO(-1.0, 13, 13) A(14) = NONZERO(-7.0, 14, 14) S=0 do i = 1, 14 ! суммирование значений s= s + A(i)%value end do write (*, '(a3, f4 .1)' ) ' s=' , s ! печать суммы end Вот результат запуска al2:~/ comm/f90exampl$ ./f90_ex7
s=59.8 Press any key to continue... Пример 8. Родовые процедуры Еще один пример с родовыми процедурами (семейством) force для работы с типами 'real_force' и 'double_force'. Определена величина force для хранения результата функции. Интерфейс описан в модуле, а реализация процедур семейства в виде программных единиц, внешних для главной программы. Исходные данные задаются оператором data. module proced_def !'force' ! 'real_force' и d 'double_force'. interface force function real_force(mass, accel) result (force) real force, mass, accel end function real_force function double_force(mass, accel) result(force) double precision force, mass, accel end function double_force end interface end module program main use proced_def ! procedure_def main. real rmass, raccel, rforce double precision dmass, daccel, dforce data rmass/2401.0/, raccel/9.81245/ data dmass/2401.0d0/, daccel/9.81245d0/ !'force'.
!'real_force', 'double_force'. rforce = force(rmass, raccel) dforce = force(dmass, daccel) write (*, '(1x, 1p, e16.9, t25, d16.9)' ) rforce, dforce end program !'force'. function real_force(mass, accel) result(force) real force, mass, accel force = mass*accel end function real_force function double_force(mass, accel) result(force) double precision force, mass, accel force = mass*accel end function double_force При запуске получился результат: al2:~/ comm/f90exampl$ ./f90_ex9 2.355969336E+04 2.355969245D+04 Press any key to continue... Пример 9. Оператор WHERE - условная обработка массивов В программе создается, изменяется и печатается массив. При внесении изменений используется оператор WHERE, обрабатывающий те элементы массива, которые удовлетворяют условию, записанному в скобках. Действие оператора эквивалентно циклу просмотра всех элеменов массива.
INTEGER :: A(2,3) DO I = 1, 2
DO J = 1, 3 A(I, J) = (I+J)*(-1)**J END DO END DO WHERE( A<0 ) A = 0 WHERE( A**2>10 ) A = 999 WHERE( A/=0 ) A = 1/A DO I = 1, 2 DO J = 1, 3 WRITE (*, 'A3,I2,A2,I2,A3,I5') ' A(', I, ', ', J, ') =', A(I, J) END DO END DO ! Оператор allocate, allocatable, allocated,deallocate, associated ! Метод для создания и выделения памяти в соответствии с образом массива PROGRAM ALLOCM INTEGER, ALLOCATABLE :: matrix( : , : ) REAL , ALLOCATABLE :: vector( : ) N=2 ALLOCATE (matrix(3,5),vector(-2:N+2)) DO I = 1, 3 DO J=1,5 matrix(i,j) = i*j END DO END DO
K = N+2 DO I = 1, K vector(I) = I END DO IF ( ALLOCATE D(matrix) ) write(*, '(a12)') 'alloc matrix' IF ( ALLOCATE D(vector) ) write(*, '(a12)') 'alloc vector' DEALLOCATE (matrix,vector) IF (.not. ALLOCATE D(matrix)) write(*, '(a15)') 'no alloc matrix' IF (.not. ALLOCATE D(vector)) write(*, '(a15)') 'no alloc vector'