Это обзор возможностей языка Fortran 95. Включены дополнительные возможности TR-15581:Enhanced Data Type Facilities, которые были реализованы повсеместно. Старые возможности, которые были заменены новыми, не описаны — немногие из этих исторических возможностей используются в современных программах, хотя большинство из них были сохранены в языке для поддержания обратной совместимости . Дополнительные возможности последующих стандартов, вплоть до Fortran 2023, описаны в стандартном документе Fortran 2023, ISO/IEC 1539-1:2023. Многие из его новых возможностей все еще реализуются в компиляторах. [1]
Fortran нечувствителен к регистру . В этой статье принято соглашение о написании ключевых слов Fortran в верхнем регистре, а всех остальных имен в нижнем; за исключением, в качестве контраста, описаний ввода/вывода (Передача данных и Операции с внешними файлами).
Основным компонентом языка Фортран является его набор символов . Его членами являются
= : + blank - * / ( ) [ ] , . $ ' ! " % & ; < > ?
Токены , имеющие синтаксическое значение для компилятора, строятся из этих компонентов. Существует шесть классов токенов:
Этикетка | 123 |
---|---|
Постоянный | 123.456789_long |
Ключевое слово | ALLOCATABLE |
Оператор | .add. |
Имя | solve_equation (до 31 символа, включая _) |
Разделитель | / ( ) (/ /) [ ] , = => : :: ; % |
Из токенов строятся утверждения . Их можно кодировать с помощью новой свободной исходной формы , которая не требует позиционирования в жесткой структуре столбцов:
FUNCTION string_concat ( s1 , s2 ) ! Это комментарий TYPE ( string ), INTENT ( IN ) :: s1 , s2 TYPE ( string ) string_concat string_concat % string_data = s1 % string_data ( 1 : s1 % length ) // & s2 % string_data ( 1 : s2 % length ) ! Это продолжение string_concat % length = s1 % length + s2 % length END FUNCTION string_concat
Обратите внимание на завершающие комментарии и завершающий знак продолжения. Может быть 39 строк продолжения и 132 символа в каждой строке. Пробелы имеют значение. Если токен или константа символа разделены на две строки:
... начало_имени ... 'очень длинная & & строка '
&
Также требуется наличие лидирующей части на продолжении линии.
Fortran имеет пять внутренних типов данных : INTEGER
, REAL
, COMPLEX
, LOGICAL
и CHARACTER
. Каждый из этих типов может быть дополнительно охарактеризован видом . Вид, по сути, определяет внутреннее представление типа: для трех числовых типов он определяет точность и диапазон, а для двух других — специфику представления хранения. Таким образом, это абстрактное понятие, которое моделирует пределы представления типов данных; оно выражается как элемент набора целых чисел (например, это может быть {1, 2, 4, 8} для целых чисел, обозначающих байты хранения), но эти значения не определены Стандартом и не переносимы. Для каждого типа существует вид по умолчанию , который используется, если вид явно не указан. Для каждого внутреннего типа существует соответствующая форма литеральной константы . Числовые типы INTEGER
и REAL
могут быть только знаковыми (понятия знака для типа нет COMPLEX
).
Целочисленные литеральные константы типа по умолчанию имеют вид
1 0 - 999 32767 + 10
Kind может быть определен как именованная константа. Если желаемый диапазон составляет ±10 kind , переносимый синтаксис для определения соответствующего kind two_bytes
—
ЦЕЛОЕ ЧИСЛО , ПАРАМЕТР :: two_bytes = SELECTED_INT_KIND ( 4 )
что позволяет впоследствии определять константы вида
- 1234_два_байта + 1_два_байта
Здесь two_bytes
— параметр типа kind; это также может быть явная целочисленная литеральная константа по умолчанию, например
- 1234_2
но такое использование непереносимо.
Функция KIND возвращает значение параметра типа kind:
ВИД ( 1 ) ВИД ( 1_два_байта )
и RANGE
функция предоставляет фактический десятичный диапазон (поэтому пользователь должен выполнить фактическое сопоставление с байтами):
ДИАПАЗОН ( 1_два_байта )
Кроме того, в операторах DATA (инициализация) могут использоваться двоичные (B), восьмеричные (O) и шестнадцатеричные (Z) константы (часто неформально называемые «константами BOZ»):
B '01010101' O '01234567' Z '10fa'
Существует как минимум два действительных типа — по умолчанию и один с большей точностью (он заменяет DOUBLE PRECISION
). SELECTED_REAL_KIND
Функции возвращают номер типа для требуемого диапазона и точности; для как минимум 9 десятичных знаков точности и диапазона от 10 −99 до 10 99 его можно указать как:
ЦЕЛОЕ ЧИСЛО , ПАРАМЕТР :: long = SELECTED_REAL_KIND ( 9 , 99 )
и литералы впоследствии указаны как
1.7_длинный
Также существуют внутренние функции
ТИП ( 1.7_long ) ТОЧНОСТЬ ( 1.7_long ) ДИАПАЗОН ( 1.7_long )
которые в свою очередь дают значение типа типа, фактическую точность (здесь не менее 9) и фактический диапазон (здесь не менее 99).
COMPLEX
Тип данных состоит из двух целочисленных или действительных компонентов:
( 1 , 3.7_длинный )
Существует только два основных значения логических констант: .TRUE.
и .FALSE.
. Здесь также могут быть разные виды. Логические не имеют собственных функций запроса видов, но используют виды, указанные для INTEGER
s; вид по умолчанию LOGICAL
такой же, как у INTEGER.
. ЛОЖЬ . . правда . _ один_байт
и KIND
функция работает так, как и ожидалось:
ВИД (. ИСТИНА .)
Формы литеральных констант для CHARACTER
типа данных:
'Строка' "Другой" ' ''''' '
(последнее — пустая строка). Разрешены различные типы (например, для различения строк ASCII и UNICODE ), но не поддерживаются широко компиляторами. Опять же, значение типа задается функцией KIND
:
ТИП ( 'ASCII' )
Числовые типы основаны на числовых моделях с соответствующими функциями запроса (чьи значения не зависят от значений их аргументов; аргументы используются только для предоставления вида). Эти функции важны для переносимого числового программного обеспечения:
DIGITS(X) | Количество значащих цифр |
EPSILON(X) | Почти ничтожно мала по сравнению с единицей (реальной) |
HUGE(X) | Наибольшее число |
MAXEXPONENT(X) | Максимальный показатель модели (действительный) |
MINEXPONENT(X) | Минимальный показатель модели (действительный) |
PRECISION(X) | Точность десятичной дроби (действительная, комплексная) |
RADIX(X) | Основа модели |
RANGE(X) | Диапазон десятичных показателей |
TINY(X) | Наименьшее положительное число (действительное) |
Скалярные переменные, соответствующие пяти внутренним типам, определяются следующим образом:
INTEGER ( KIND = 2 ) :: i REAL ( KIND = long ) :: a COMPLEX :: current LOGICAL :: Pravda CHARACTER ( LEN = 20 ) :: word CHARACTER ( LEN = 2 , KIND = Kanji ) :: kanji_word
где необязательный KIND
параметр указывает нестандартный вид, а ::
нотация отделяет тип и атрибуты от имени(й) переменной(ых) и их необязательных начальных значений, что позволяет вводить полную спецификацию и инициализацию переменных в одном операторе (в предыдущих стандартах атрибуты и инициализаторы приходилось объявлять в нескольких операторах). Хотя в приведенных выше примерах это не требуется (поскольку нет дополнительных атрибутов и инициализации), большинство программистов на Fortran-90 привыкают использовать его везде.
LEN=
спецификатор применим только к CHARACTER
s и указывает длину строки (заменяя старую *len
форму). Явные KIND=
и LEN=
спецификаторы необязательны:
ХАРАКТЕР ( 2 , Кандзи ) :: kanji_word
работает так же хорошо.
Есть еще несколько интересных особенностей характера. Так же как подстрока, как в
СИМВОЛ ( 80 ) :: строка ... = строка ( i : i ) ! подстрока
раньше было возможно, теперь подстрока
'0123456789' ( я : я )
Также допускаются строки нулевой длины:
строка ( i : i - 1 ) ! строка нулевой длины
Наконец, существует набор внутренних функций характера, примерами которых являются
ACHAR | IACHAR (для набора ASCII) |
ADJUSTL | ADJUSTR |
LEN_TRIM | INDEX(s1, s2, BACK=.TRUE.) |
REPEAT | SCAN (за один комплект) |
TRIM | VERIFY (для всего набора) |
Для производных типов данных сначала необходимо определить форму типа:
ТИП персона ПЕРСОНАЖ ( 10 ) имя РЕАЛЬНЫЙ возраст КОНЕЦ ТИП персона
и затем можно определить переменные этого типа:
ТИП ( человек ) ты , я
Для выбора компонентов производного типа %
используется квалификатор:
ваш % возраст
Литеральные константы производных типов имеют вид TypeName(1stComponentLiteral, 2ndComponentLiteral, ...)
:
вы = человек ( 'Смит' , 2 3,5 )
который известен как конструктор структур . Определения могут ссылаться на ранее определенный тип:
ТИП точка REAL x , y КОНЕЦ ТИП точка ТИП треугольник ТИП ( точка ) a , b , c КОНЕЦ ТИП треугольник
и для переменной типа треугольник, как в
ТИП ( треугольник ) т
каждый компонент типа point
доступен как
т % а т % б т % в
которые, в свою очередь, имеют конечные компоненты типа real:
т % а % х т % а % у т % б % х и т.д.
(Обратите внимание, что %
был выбран квалификатор вместо точки ( .
) из-за потенциальной неоднозначности с записью оператора, например .OR.
).
Если не указано иное, все переменные, начинающиеся с букв I, J, K, L, M и N, являются переменными по умолчанию INTEGER
s, а все остальные — переменными по умолчанию REAL
; другие типы данных должны быть явно объявлены. Это известно как неявная типизация и является наследием ранних дней FORTRAN. Эти значения по умолчанию могут быть переопределены с помощью IMPLICIT TypeName (CharacterRange)
операторов, например:
НЕЯВНЫЙ КОМПЛЕКС ( Z ) НЕЯВНЫЙ ХАРАКТЕР ( A - B ) НЕЯВНЫЙ ВЕЩЕСТВЕННЫЙ ( C - H , N - Y )
Однако хорошей практикой является явное указание всех переменных, и этого можно добиться, вставив оператор IMPLICIT NONE
в начало каждого программного модуля.
Массивы считаются переменными сами по себе. Каждый массив характеризуется своим типом , рангом и формой (которая определяет пределы каждого измерения). Границы каждого измерения по умолчанию равны 1 и размеру , но произвольные границы могут быть явно указаны. DIMENSION
ключевое слово является необязательным и считается атрибутом; если оно опущено, форма массива должна быть указана после имени переменной-массива. Например,
REAL :: a ( 10 ) INTEGER , РАЗМЕРНОСТЬ ( 0 : 100 , - 50 : 50 ) :: карта
объявляет два массива, rank-1 и rank-2, элементы которых располагаются в порядке столбцов . Элементы, например,
а ( 1 ) а ( я * дж )
и являются скалярами. Индексы могут быть любым скалярным целочисленным выражением.
Разделы являются частями переменных массива и сами по себе являются массивами:
a ( i : j ) ! ранг один map ( i : j , k : l : m ) ! ранг два a ( map ( i , k : l )) ! индекс вектора a ( 3 : 2 ) ! нулевая длина
Целые массивы и секции массивов являются объектами, имеющими значения массива. Константы, имеющие значения массива (конструкторы), доступны, заключенные в (/ ... /)
:
( / 1 , 2 , 3 , 4 / ) ( / ( ( / 1 , 2 , 3 / ), я = 1 , 4 ) / ) ( / ( я , я = 1 , 9 , 2 ) / ) ( / ( 0 , я = 1 , 100 ) / ) ( / ( 0,1 * я , я = 1 , 10 ) / )
используя подразумеваемую нотацию цикла DO. Fortran 2003 позволяет использовать скобки: [1, 2, 3, 4]
и [([1,2,3], i=1,4)]
вместо первых двух примеров выше, и многие компиляторы теперь поддерживают это. Производный тип данных может, конечно, содержать компоненты массива:
ТИП триплет REAL , РАЗМЕРНОСТЬ ( 3 ) :: вершина КОНЕЦ ТИП триплет ТИП ( триплет ), РАЗМЕРНОСТЬ ( 4 ) :: t
так что
t(2)
является скаляром (структурой)t(2)%vertex
является компонентом массива скаляраПеременным могут быть присвоены начальные значения, указанные в спецификации:
РЕАЛЬНЫЙ , РАЗМЕРНОСТЬ ( 3 ) :: a = ( / 0,1 , 0,2 , 0,3 / )
и компоненту производного типа данных может быть присвоено начальное значение по умолчанию:
ТИП триплет REAL , РАЗМЕРНОСТЬ ( 3 ) :: вершина = 0.0 КОНЕЦ ТИП триплет
Когда локальные переменные инициализируются внутри процедуры, они неявно приобретают атрибут SAVE:
РЕАЛЬНЫЙ , РАЗМЕРНОСТЬ ( 3 ) :: точка = ( / 0,0 , 1,0 , - 1,0 / )
Это заявление эквивалентно
РЕАЛЬНЫЙ , РАЗМЕР ( 3 ), СОХРАНИТЬ :: точка = ( / 0.0 , 1.0 , - 1.0 / )
для локальных переменных внутри подпрограммы или функции. Атрибут SAVE заставляет локальные переменные сохранять свое значение после вызова процедуры, а затем инициализировать переменную сохраненным значением при возврате в процедуру.
Именованную константу можно указать напрямую, добавив PARAMETER
атрибут и значения константы к оператору типа:
REAL , РАЗМЕРНОСТЬ ( 3 ) , ПАРАМЕТР :: поле = ( /0.,1.,2./ ) ТИП ( триплет ) , ПАРАМЕТР :: t = триплет ( ( /0.,0.,0./ ) )
Оператор DATA
может использоваться для скаляров, а также для массивов и переменных производного типа. Это также единственный способ инициализировать только части таких объектов, а также инициализировать двоичными, восьмеричными или шестнадцатеричными значениями:
ТИП ( триплет ) :: t1 , t2 ДАННЫЕ t1 / триплет ( ( / 0. , 1. , 2. / ) ) / , t2 % вершина ( 1 ) / 12 3. / ДАННЫЕ массив ( 1 : 64 ) / 64 * 0 / ДАННЫЕ i , j , k / B '01010101' , O '77' , Z 'ff' /
Значения, используемые в операторах DATA
и PARAMETER
или с этими атрибутами, являются константными выражениями, которые могут включать ссылки на: конструкторы массивов и структур, элементарные встроенные функции с целочисленными или символьными аргументами и результатами, а также шесть трансформационных функций REPEAT, SELECTED_INT_KIND, TRIM, SELECTED_REAL_KIND, RESHAPE
и TRANSFER
(см. Встроенные процедуры):
ЦЕЛОЕ ЧИСЛО , ПАРАМЕТР :: long = SELECTED_REAL_KIND ( 12 ), & массив ( 3 ) = ( / 1 , 2 , 3 / )
Можно указать сведения о переменных, используя любое неконстантное, скалярное, целочисленное выражение, которое также может включать ссылки на функции запроса:
ПОДПРОГРАММА s ( b , m , c ) ИСПОЛЬЗОВАТЬ mod ! содержит a REAL , DIMENSION (:, :) :: b REAL , DIMENSION ( UBOUND ( b , 1 ) + 5 ) :: x INTEGER :: m CHARACTER ( LEN =* ) :: c CHARACTER ( LEN = m + LEN ( c )) :: cc REAL ( SELECTED_REAL_KIND ( 2 * PRECISION ( a ))) :: z
Доступны обычные арифметические операторы +, -, *, /, **
(приведены здесь в порядке возрастания приоритета).
Скобки используются для указания порядка оценки, где это необходимо:
а * б + в ! * первый а * ( б + в ) ! + первый
Правила для скалярных числовых выражений и присваиваний учитывают нестандартные типы. Таким образом, смешанные числовые выражения и правила присваивания включают различные параметры типа типа ожидаемым образом:
real2 = integer0 + real1
преобразуется integer0
в действительное значение того же рода, что и real1
; результат имеет тот же род и преобразуется в вид real2
для присваивания.
Эти функции доступны для контролируемого округления действительных чисел до целых:
NINT
: округлить до ближайшего целого числа, вернуть целочисленный результатANINT
: округлить до ближайшего целого числа, вернуть действительный результатINT
: усечение (округление в сторону нуля), возврат целого результатаAINT
: усечение (округление в сторону нуля), возврат действительного результатаCEILING
: наименьшее целое значение, не меньшее аргумента (округление вверх) (Fortran-90)FLOOR
: наибольшее целое значение, не большее аргумента (округлено в меньшую сторону) (Fortran-90)Для скалярных реляционных операций числовых типов существует набор встроенных операторов:
< <= == /= > >=.LT. .LE. .EQ. .NE. .GT. .GE.
(формы выше являются новыми для Fortran-90, а более старые эквивалентные формы приведены ниже). Примеры выражений:
a < b . AND . i /= j ! для числовых переменных flag = a == b ! для логических переменных flags
В случае скалярных символов и заданныхCHARACTER(8) result
это законно писать
результат ( 3 : 5 ) = результат ( 1 : 3 ) ! перекрытие разрешено результат ( 3 : 3 ) = результат ( 3 : 2 ) ! нет назначения нулевой строки
Конкатенация выполняется оператором '//'.
результат = 'abcde' // '123' имя файла = результат // '.dat'
Никаких встроенных операций (кроме присваивания, определенного на покомпонентной основе) не существует между производными типами данных, взаимно или с внутренними типами. Однако значение существующих или указанных пользователем операторов может быть (пере)определено:
ТИП string80 INTEGER длина CHARACTER ( 80 ) значение КОНЕЦ ТИП string80 CHARACTER :: char1 , char2 , char3 ТИП ( string80 ) :: str1 , str2 , str3
мы можем написать
str3 = str1 // str2 ! необходимо определить операцию str3 = str1 . concat . str2 ! необходимо определить операцию char3 = char2 // char3 ! только внутренний оператор str3 = char1 ! необходимо определить присваивание
Обратите внимание на « перегруженное » использование внутреннего символа //
и именованного оператора .concat.
. Разница между двумя случаями заключается в том, что для токена внутреннего оператора применяются обычные правила приоритета, тогда как для именованных операторов приоритет является наивысшим как у унарного оператора или низшим как у бинарного. В
вектор3 = матрица * вектор1 + вектор2 вектор3 = ( матрица . умножить на . вектор1 ) + вектор2
эти два выражения эквивалентны только в том случае, если добавлены соответствующие скобки, как показано. В каждом случае в модуле должны быть определены процедуры, определяющие оператор и присваивание, а также соответствующая ассоциация оператор-процедура, как показано ниже:
ОПЕРАТОР ИНТЕРФЕЙСА ( // ) !Перегружает оператор // как вызывающий процедуру string_concat МОДУЛЬ ПРОЦЕДУРА string_concat КОНЕЦ ИНТЕРФЕЙСА
Функция конкатенации строк — это более сложная версия той, что уже была показана в Basics. Обратите внимание, что для обработки состояния ошибки, которое возникает, когда две строки вместе превышают предустановленный предел в 80 символов, было бы безопаснее использовать подпрограмму для выполнения конкатенации (в этом случае перегрузка операторов не будет применима).
МОДУЛЬ string_type НЕЯВНЫЙ НЕТ ТИП string80 ЦЕЛОЕ число длина СИМВОЛ ( LEN = 80 ) :: string_data КОНЕЦ ТИП string80 НАЗНАЧЕНИЕ ИНТЕРФЕЙСА ( = ) МОДУЛЬ ПРОЦЕДУРА c_to_s_assign , s_to_c_assign КОНЕЦ ИНТЕРФЕЙС ОПЕРАТОР ИНТЕРФЕЙСА ( // ) МОДУЛЬ ПРОЦЕДУРА string_concat КОНЕЦ ИНТЕРФЕЙС СОДЕРЖИТ ПОДПРОГРАММУ c_to_s_assign ( s , c ) ТИП ( string80 ), НАМЕРЕНИЕ ( OUT ) :: s СИМВОЛ ( LEN =* ), НАМЕРЕНИЕ ( IN ) :: c s % string_data = c s % длина = LEN ( c ) КОНЕЦ ПОДПРОГРАММА c_to_s_assign ПОДПРОГРАММА s_to_c_assign ( c , s ) ТИП ( string80 ), INTENT ( IN ) :: s CHARACTER ( LEN =* ), INTENT ( OUT ) :: c c = s % string_data ( 1 : s % length ) КОНЕЦ ПОДПРОГРАММЫ s_to_c_assign ТИП ( string80 ) ФУНКЦИЯ string_concat ( s1 , s2 ) ТИП ( string80 ), INTENT ( IN ) :: s1 , s2 ТИП ( string80 ) :: s ЦЕЛОЕ ЧИСЛО :: n1 , n2 СИМВОЛ ( 160 ) :: ctot n1 = LEN_TRIM ( s1 % string_data ) n2 = LEN_TRIM ( s2 % string_data ) IF ( n1 + n2 <= 80 ) then s % string_data = s1 % string_data ( 1 : n1 ) // s2 % string_data ( 1 : n2 ) ELSE ! Это ошибочное состояние, которое следует обработать - сейчас просто усекаем ctot = s1 % string_data ( 1 : n1 ) // s2 % string_data ( 1 : n2 ) s % string_data = ctot ( 1 : 80 ) END IF s % length = LEN_TRIM ( s % string_data ) string_concat = s END FUNCTION string_concat END MODULE string_type ПРОГРАММА main USE string_type TYPE ( string80 ) :: s1 , s2 , s3 CALL c_to_s_assign ( s1 , 'Меня зовут' ) CALL c_to_s_assign ( s2 , 'Линус Торвальдс' ) s3 = s1 // s2 WRITE ( * , * ) 'Результат: ' , s3 % string_data WRITE ( * , * ) 'Длина: ' , s3 % length КОНЕЦ ПРОГРАММЫ
Определенные операторы, подобные этим, требуются для выражений, которые также разрешены в конструкторах структур (см. Производные типы данных):
str1 = string ( 2 , char1 // char2 ) ! конструктор структуры
В случае массивов, если они имеют одинаковую форму (согласующиеся), операции и присваивания расширяются очевидным образом, поэлементно. Например, данные объявления
REAL , РАЗМЕРНОСТЬ ( 10 , 20 ) :: a , b , c REAL , РАЗМЕРНОСТЬ ( 5 ) :: v , w ЛОГИЧЕСКИЙ флаг ( 10 , 20 )
можно записать:
a = b ! присваивание всего массива c = a / b ! деление всего массива и присваивание c = 0. ! присваивание всего массива скалярному значению w = v + 1. ! сложение всего массива со скалярным значением w = 5 / v + a ( 1 : 5 , 5 ) ! деление массива и сложение с разделом flag = a == b ! проверка отношения всего массива и присваивание c ( 1 : 8 , 5 : 10 ) = a ( 2 : 9 , 5 : 10 ) + b ( 1 : 8 , 15 : 20 ) ! сложение и присваивание раздела массива v ( 2 : 5 ) = v ( 1 : 4 ) ! присваивание перекрывающегося раздела
Порядок оценки выражений не указан, чтобы обеспечить оптимизацию на параллельных и векторных машинах. Конечно, любые операторы для массивов производного типа должны быть определены.
Некоторые реальные внутренние функции, которые полезны для числовых вычислений:
CEILING
FLOOR
MODULO
(также целое число)EXPONENT
FRACTION
NEAREST
RRSPACING
SPACING
SCALE
SET_EXPONENT
Это массив значений для аргументов массива (элементарные), как и все функции FORTRAN 77 (кроме LEN):
INT
REAL
CMPLX
AINT
ANINT
NINT
ABS
MOD
SIGN
DIM
MAX
MIN
Степени, логарифмы и тригонометрические функции
SQRT
EXP
LOG
LOG10
SIN
COS
TAN
ASIN
ACOS
ATAN
ATAN2
SINH
COSH
TANH
Комплексные числа:
AIMAG
CONJG
Ниже приведены данные для персонажей:
LGE
LGT
LLE
LLT
ICHAR
CHAR
INDEX
Простая GO TO
метка существует, но ее обычно избегают — в большинстве случаев более конкретная конструкция ветвления позволит реализовать ту же логику с большей ясностью.
Простейшим условным тестом является IF
утверждение:IF (a > b) x = y
Полноценная IF
конструкция проиллюстрирована
ЕСЛИ ( i < 0 ) ТО ЕСЛИ ( j < 0 ) ТО x = 0. ИНАЧЕ z = 0. КОНЕЦ ЕСЛИ ИНАЧЕ ЕСЛИ ( k < 0 ) ТО z = 1. ИНАЧЕ x = 1. КОНЕЦ ЕСЛИ
Эта CASE
конструкция является заменой вычисляемой GOTO
, но лучше структурирована и не требует использования меток операторов:
SELECT CASE ( number ) ! number of type integer CASE (: - 1 ) ! all values less 0 n_sign = - 1 CASE ( 0 ) ! only 0 n_sign = 0 CASE ( 1 :) ! all values more 0 n_sign = 1 END SELECT
Каждый CASE
список селекторов может содержать список и/или диапазон целых чисел, символьных или логических констант, значения которых не могут перекрываться внутри или между селекторами:
ДЕЛО ( 1 , 2 , 7 , 10 : 17 , 23 )
Доступно значение по умолчанию:
СЛУЧАЙ ПО УМОЛЧАНИЮ
Существует только одна оценка и только одно совпадение.
Упрощенная, но достаточная форма конструкции DO
проиллюстрирована следующим образом:
внешний : DO внутренний : DO i = j , k , l ! от j до k с шагом l (l необязательно) : IF (...) CYCLE : IF (...) EXIT внешний : END DO внутренний END DO внешний
где мы отмечаем, что циклы могут быть необязательно названы, так что любой оператор EXIT или CYCLE может указывать, какой цикл имеется в виду.
Многие, но не все, простые циклы можно заменить выражениями массива и присваиваниями или новыми встроенными функциями. Например
tot = 0. DO i = m , n tot = tot + a ( i ) КОНЕЦ DO
становится простоtot = SUM( a(m:n) )
Для обсуждения этой темы нам нужны некоторые определения. В логических терминах исполняемая программа состоит из одной основной программы и нуля или более подпрограмм (или процедур ) — они что-то делают. Подпрограммы — это либо функции , либо подпрограммы , которые являются внешними, внутренними или модульными подпрограммами. (Внешние подпрограммы — это то, что мы знали из FORTRAN 77.)
Однако с организационной точки зрения полная программа состоит из программных единиц . Это либо основные программы, либо внешние подпрограммы или модули , и они могут быть скомпилированы отдельно.
Пример основной (и полной) программы:
ПРОГРАММА тест ПЕЧАТЬ * , 'Привет, мир!' КОНЕЦ ПРОГРАММЫ тест
Примером основной программы и внешней подпрограммы, образующих исполняемую программу, является
ПРОГРАММА test CALL print_message КОНЕЦ ПРОГРАММЫ test SUBROUTINE print_message ПЕЧАТЬ * , 'Привет, мир!' КОНЕЦ ПОДПРОГРАММЫ print_message
Форма функции:
ФУНКЦИЯ имя ( arg1 , arg2 ) ! ноль или более аргументов : имя = ... : END ФУНКЦИЯ имя
Форма ссылки функции:x = name(a, b)
Внутренняя подпрограмма содержится одна в другой (максимум на одном уровне вложенности) и обеспечивает замену функции оператора:
ПОДПРОГРАММА внешняя REAL x , y : СОДЕРЖИТ ПОДПРОГРАММУ внутренняя REAL y y = x + 1. : КОНЕЦ ПОДПРОГРАММЫ внутренняя ! ПОДПРОГРАММА обязательна КОНЕЦ ПОДПРОГРАММЫ внешняя
Мы говорим, что является outer
хостом иinner
что inner
получает доступ к сущностям в outer
посредством ассоциации с хостом (например, с x
), тогда как y
является локальной переменной для inner
.
Областью действия именованной сущности является единица области действия , в данном случае outer
меньше inner
и inner
.
Имена программных модулей и внешних процедур являются глобальными , а имена подразумеваемых переменных DO имеют область действия оператора, который их содержит.
Модули используются для упаковки
Примером модуля, содержащего определение типа, блок интерфейса и функциональную подпрограмму, является
MODULE interval_arithmetic ТИП интервал REAL нижний , верхний КОНЕЦ ТИП интервал ИНТЕРФЕЙС ОПЕРАТОР ( + ) МОДУЛЬ ПРОЦЕДУРА add_intervals КОНЕЦ ИНТЕРФЕЙС : СОДЕРЖИТ ФУНКЦИЮ add_intervals ( a , b ) ТИП ( интервал ), НАМЕРЕНИЕ ( В ) :: a , b ТИП ( интервал ) add_intervals add_intervals % нижний = a % нижний + b % нижний add_intervals % верхний = a % верхний + b % верхний КОНЕЦ ФУНКЦИЯ add_intervals ! ФУНКЦИЯ обязательная : КОНЕЦ МОДУЛЬ interval_arithmetic
и простое утверждение
ИСПОЛЬЗОВАТЬ интервальную арифметику
обеспечивает использование ассоциации для всех сущностей модуля. Подпрограммы модуля могут, в свою очередь, содержать внутренние подпрограммы.
Атрибуты PUBLIC
и PRIVATE
используются в спецификациях в модулях для ограничения области действия сущностей. Форма атрибута:
REAL , PUBLIC :: x , y , z ! default INTEGER , PRIVATE :: u , v , w
и форма заявления
ОБЩЕСТВЕННЫЙ :: x , y , z , ОПЕРАТОР (. add .) ЧАСТНЫЙ :: u , v , w , ПРИСВОЕНИЕ ( = ), ОПЕРАТОР ( * )
Форму заявления необходимо использовать для ограничения доступа операторов, а также ее можно использовать для изменения общего значения по умолчанию:
PRIVATE ! устанавливает значение по умолчанию для модуля PUBLIC :: only_this
Для производных типов существует три возможности: тип и его компоненты являются PUBLIC, тип является PUBLIC, а его компоненты — PRIVATE (видим только тип, и можно легко изменить его данные), или все они являются PRIVATE (только для внутреннего использования в модуле):
МОДУЛЬ мой ЧАСТНЫЙ ТИП , ОБЩИЙ :: список РЕАЛЬНЫЙ x , y ТИП ( список ), УКАЗАТЕЛЬ :: следующий КОНЕЦ ТИП список ТИП ( список ) :: дерево : КОНЕЦ МОДУЛЬ мой
Целью оператора USE
является получение доступа к сущностям в модуле. Он имеет опции для разрешения конфликтов имен, если импортированное имя совпадает с локальным:
ИСПОЛЬЗУЙТЕ мой , local_list => список
или ограничить используемые сущности указанным набором:
ИСПОЛЬЗУЙТЕ мой , ТОЛЬКО : список
Их можно комбинировать:
ИСПОЛЬЗУЙТЕ мой , ТОЛЬКО : local_list => list
Мы можем указать цель фиктивных аргументов:
ПОДПРОГРАММА перетасовки ( ncards , карты ) ЦЕЛОЕ ЧИСЛО , НАМЕРЕНИЕ ( ВХОД ) :: ncards ЦЕЛОЕ ЧИСЛО , НАМЕРЕНИЕ ( ВЫХОД ), РАЗМЕР ( ncards ) :: карты
Также возможен INOUT: здесь фактический аргумент должен быть переменной (в отличие от случая по умолчанию, где он может быть константой).
Аргументы могут быть необязательными:
ПОДПРОГРАММА mincon ( n , f , x , upper , lower , equalities , inequalityities , convex , xstart ) REAL , OPTIONAL , DIMENSION :: upper , lower : IF ( PRESENT ( lower )) THEN ! проверка на наличие фактического аргумента :
позволяет нам звонить mincon
по
CALL mincon ( n , f , x , верхний )
Аргументы могут быть ключевыми словами, а не позиционными (которые идут первыми):
CALL mincon ( n , f , x , равенства = 0 , xstart = x0 )
Необязательные и ключевые аргументы обрабатываются явными интерфейсами, то есть с помощью внутренних или модульных процедур или с помощью интерфейсных блоков.
Любая ссылка на внутреннюю или модульную подпрограмму осуществляется через интерфейс, который является «явным» (то есть компилятор может видеть все детали). Ссылка на внешнюю (или фиктивную) процедуру обычно является «неявной» (компилятор предполагает детали). Однако в этом случае мы также можем предоставить явный интерфейс. Это копия заголовка, спецификаций и оператора END соответствующей процедуры, либо помещенная в модуль, либо вставленная напрямую:
REAL FUNCTION minimum ( a , b , func ) ! возвращает минимальное значение функции func(x) ! в интервале (a,b) REAL , INTENT ( in ) :: a , b INTERFACE REAL FUNCTION func ( x ) REAL , INTENT ( IN ) :: x END FUNCTION func END INTERFACE REAL f , x : f = func ( x ) ! вызов пользовательской функции. : END FUNCTION minimum
Явный интерфейс обязателен для
Он позволяет проводить полную проверку во время компиляции между фактическими и фиктивными аргументами.
В общем случае, лучший способ гарантировать, что интерфейс процедуры является явным, — это поместить соответствующую процедуру в модуль или использовать ее как внутреннюю процедуру.
Интерфейсные блоки предоставляют механизм, с помощью которого мы можем определять общие имена для конкретных процедур:
INTERFACE gamma ! общее имя FUNCTION sgamma ( X ) ! конкретное имя REAL ( SELECTED_REAL_KIND ( 6 )) sgamma , x END FUNCTION dgamma ( X ) ! конкретное имя REAL ( SELECTED_REAL_KIND ( 12 )) dgamma , x END END INTERFACE
где заданный набор конкретных имен, соответствующих общему имени, должен быть всеми функциями или всеми подпрограммами. Если этот интерфейс находится внутри модуля, то он просто
ИНТЕРФЕЙС гамма МОДУЛЬ ПРОЦЕДУРА sgamma , dgamma КОНЕЦ ИНТЕРФЕЙС
Мы можем использовать существующие имена, например, SIN, и компилятор подберет правильную ассоциацию.
Мы уже видели использование интерфейсных блоков для определенных операторов и назначений (см. Модули).
Косвенная рекурсия полезна для многомерной интеграции.
объем = интегрировать ( fy , ybounds )
Мы могли бы иметь
РЕКУРСИВНАЯ ФУНКЦИЯ интегрировать ( f , bounds ) ! Интегрировать f(x) от bounds(1) до bounds(2) REAL интегрировать INTERFACE FUNCTION f ( x ) REAL f , x END FUNCTION f END INTERFACE REAL , DIMENSION ( 2 ), INTENT ( IN ) :: bounds : END FUNCTION интегрировать
и проинтегрировать f(x, y) по прямоугольнику:
ФУНКЦИЯ fy ( y ) ИСПОЛЬЗОВАТЬ func ! модуль func содержит функцию f REAL fy , y yval = y fy = integrated ( f , xbounds ) END
Прямая рекурсия — это когда процедура вызывает сама себя, как в
РЕКУРСИВНАЯ ФУНКЦИЯ факториал ( n ) РЕЗУЛЬТАТ ( res ) ЦЕЛОЕ ЧИСЛО res , n ЕСЛИ ( n . EQ . 0 ) ТО res = 1 ИНАЧЕ res = n * факториал ( n - 1 ) КОНЕЦ ЕСЛИ КОНЕЦ
Здесь мы отмечаем RESULT
условие и тест на прекращение.
Это функция параллельных вычислений.
В операторе и конструкции FORALL любые побочные эффекты в функции могут помешать оптимизации на параллельном процессоре – порядок выполнения назначений может повлиять на результаты. Чтобы контролировать эту ситуацию, мы добавляем ключевое PURE
слово к оператору SUBROUTINE
or FUNCTION
– утверждение, что процедура (выраженная просто):
SAVE
атрибутом, который сохраняет значения между вызовами), иКомпилятор может проверить, что это так, например:
ЧИСТАЯ ФУНКЦИЯ вычислить ( x )
Все внутренние функции являются чистыми.
Обработка массивов включена в Fortran по двум основным причинам:
В то же время были добавлены серьезные расширения функционала в этой области. Целые массивы мы уже встречали выше #Arrays 1 и здесь #Arrays 2 - теперь развиваем тему.
Массив нулевого размера обрабатывается Фортраном как законный объект, без специального кодирования программистом. Таким образом, в
ДЕЛАТЬ i = 1 , n x ( i ) = b ( i ) / a ( i , i ) b ( i + 1 : n ) = b ( i + 1 : n ) - a ( i + 1 : n , i ) * x ( i ) КОНЕЦ ДЕЛАТЬ
Для последней итерации, где , не требуется специального кода i = n
. Отметим, что массив нулевого размера считается определенным; однако массив формы (0,2) не согласуется с массивом формы (0,3), тогда как является допустимым оператором «ничего не делать».x(1:0) = 3
Это расширение и замена для массивов предполагаемого размера. При наличии фактического аргумента, например:
РЕАЛЬНЫЙ , РАЗМЕРНОСТЬ ( 0:10 , 0:20 ) :: a : ВЫЗОВ подпрограммы ( a )
соответствующая спецификация фиктивного аргумента определяет только тип и ранг массива, а не его форму. Эта информация должна быть предоставлена явным интерфейсом, часто с использованием блока интерфейса (см. Блоки интерфейса). Таким образом, мы пишем просто
ПОДПРОГРАММА sub ( da ) REAL , РАЗМЕР (:, :) :: da
и это как если бы da
были размерными (11,21). Однако мы можем указать любую нижнюю границу, и массив отобразится соответствующим образом.
РЕАЛЬНЫЙ , РАЗМЕРНОСТЬ ( 0 :, 0 :) :: da
Передается форма, а не границы, где нижняя граница по умолчанию равна 1, а верхняя граница по умолчанию — соответствующему размеру.
Частичная замена использованного EQUIVALENCE
предоставляется этой возможностью, полезной для локальных временных массивов, как в
ПОДПРОГРАММА swap ( a , b ) REAL , DIMENSION (:) :: a , b REAL , DIMENSION ( SIZE ( a )) :: work work = a a = b b = work КОНЕЦ ПОДПРОГРАММЫ swap
Фактическое хранилище обычно размещается в стеке.
Fortran обеспечивает динамическое распределение памяти; он опирается на механизм хранения кучи (и заменяет другое использование EQUIVALENCE
). Примером создания рабочего массива для всей программы является
MODULE work_array INTEGER n REAL , DIMENSION (:,:,:), ALLOCATABLE :: work КОНЕЦ МОДУЛЬНОЙ ПРОГРАММЫ main USE work_array READ ( input , * ) n ALLOCATE ( work ( n , 2 * n , 3 * n ), STAT = status ) : DEALLOCATE ( work )
Рабочий массив может быть распространен по всей программе с помощью USE
оператора в каждом программном модуле. Мы можем указать явную нижнюю границу и выделить несколько сущностей в одном операторе. Чтобы освободить мертвое хранилище, мы пишем, например,
ОСВОБОДИТЬ ( a , b )
Освобождение массивов происходит автоматически, когда они выходят из области видимости.
Мы уже встречались с присваиваниями и операциями целых массивов:
REAL , DIMENSION ( 10 ) :: a , b a = 0. ! скалярная трансляция; элементное присваивание b = SQRT ( a ) ! результат встроенной функции как объект массива
Во втором назначении встроенная функция возвращает массив-значный результат для массив-значного аргумента. Мы можем сами написать функции массив-значные (они требуют явного интерфейса):
PROGRAM test REAL , DIMENSION ( 3 ) :: a = ( / 1. , 2. , 3. / ), & b = ( / 2. , 2. , 2. / ), r r = f ( a , b ) PRINT * , r СОДЕРЖИТ ФУНКЦИЮ f ( c , d ) REAL , DIMENSION (:) :: c , d REAL , DIMENSION ( SIZE ( c )) :: f f = c * d ! (или какая-то более полезная функция c и d) КОНЕЦ ФУНКЦИИ f КОНЕЦ ПРОГРАММЫ test
Элементарные процедуры указываются со скалярными фиктивными аргументами, которые могут быть вызваны с фактическими аргументами массива. В случае функции форма результата является формой аргументов массива.
Большинство встроенных функций являются элементарными, и Fortran 95 расширяет эту возможность на невстроенные процедуры, тем самым обеспечивая эффект написания на Fortran 90 22 различных версий для рангов 0-0, 0-1, 1-0, 1-1, 0-2, 2-0, 2-2, ... 7-7, и является дополнительным средством оптимизации на параллельных процессорах. Элементарная процедура должна быть чистой.
ЭЛЕМЕНТАРНАЯ ПОДПРОГРАММА swap ( a , b ) REAL , INTENT ( INOUT ) :: a , b REAL :: work work = a a = b b = work КОНЕЦ ПОДПРОГРАММЫ swap
Фиктивные аргументы не могут использоваться в выражениях спецификации (см. выше), за исключением аргументов определенных внутренних функций ( BIT_SIZE
, KIND
, LEN
, и числовых функций запроса (см. ниже).
Часто нам нужно замаскировать назначение. Это можно сделать с помощью WHERE
, либо как оператор:
ГДЕ ( a /= 0.0 ) a = 1.0 / a ! избегайте деления на 0
(примечание: проверка выполняется поэлементно, а не по всему массиву) или как конструкция:
WHERE ( a /= 0.0 ) a = 1.0 / a b = a ! все массивы одинаковой формы END WHERE
или
ГДЕ ( a /= 0.0 ) a = 1.0 / a В ДРУГОМ МЕСТЕ a = ОГРОМНЫЙ ( a ) КОНЕЦ ГДЕ
Дальше:
WHERE
утверждение конструкции WHERE
, но и любое ELSEWHERE
утверждение, которое она содержит;WHERE
может содержать любое количество замаскированных ELSEWHERE
операторов, но не более одного ELSEWHERE
оператора без маски, и он должен быть последним;WHERE
конструкции могут быть вложены друг в друга, просто FORALL
конструкции;WHERE
присваивания может быть определенным присваиванием при условии, что он является элементарным;WHERE
может быть названа так же, как и другие конструкции.При DO
выполнении конструкции каждая последующая итерация выполняется по порядку и одна за другой, что затрудняет оптимизацию на параллельном процессоре.
ДЛЯ ВСЕХ ( i = 1 : n ) a ( i , i ) = x ( i )
где отдельные присваивания могут выполняться в любом порядке и даже одновременно. Их FORALL
можно рассматривать как присваивание массива, выраженное с помощью индексов.
ДЛЯ ВСЕХ ( i = 1 : n , j = 1 : n , y ( i , j ) /= 0. ) x ( j , i ) = 1.0 / y ( i , j )
с маскирующим состоянием.
Конструкция FORALL
позволяет выполнить несколько операторов присваивания по порядку.
а ( 2 : n - 1 , 2 : n - 1 ) = а ( 2 : n - 1 , 1 : n - 2 ) + а ( 2 : n - 1 , 3 : n ) + а ( 1 : n - 2 , 2 : n - 1 ) + а ( 3 : n , 2 : n - 1 ) б ( 2 : n - 1 , 2 : n - 1 ) = а ( 2 : n - 1 , 2 : n - 1 )
эквивалентно присваиванию массива
ДЛЯ ВСЕХ ( i = 2 : n - 1 , j = 2 : n - 1 ) a ( i , j ) = a ( i , j - 1 ) + a ( i , j + 1 ) + a ( i - 1 , j ) + a ( i + 1 , j ) b ( i , j ) = a ( i , j ) КОНЕЦ ДЛЯ ВСЕХ
Версия FORALL
более читабельна.
Присваивание в a FORALL
похоже на присваивание массива: как если бы все выражения были оценены в любом порядке, удерживались во временном хранилище, затем все присваивания выполнялись в любом порядке. Первое выражение должно полностью завершиться, прежде чем второе может начаться.
A FORALL
может быть вложенным и может включать в себя WHERE
. Процедуры, на которые ссылаются внутри , FORALL
должны быть чистыми.
Для простого случая, учитывая
РЕАЛЬНЫЙ , РАЗМЕРНОСТЬ ( 100 , 100 ) :: а
мы можем ссылаться на один элемент, например, a(1, 1)
. Для производного типа данных, например
ТИП fun_del REAL u REAL , РАЗМЕР ( 3 ) :: du КОНЕЦ ТИП fun_del
мы можем объявить массив такого типа:
ТИП ( fun_del ), РАЗМЕР ( 10 , 20 ) :: tar
и ссылка типа — это элемент (скаляр!) типа fun_del, но это массив типа real, и является его элементом. Основное правило, которое следует запомнить, заключается в том, что элемент массива всегда имеет индекс или индексы, квалифицирующие как минимум фамилию.tar(n, 2)
tar(n, 2)%du
tar(n, 2)%du(2)
Общая форма индекса для раздела массива:
[ нижний ] : [ верхний ] [ : шаг ]
(где [ ] указывает на необязательный элемент), как в
REAL a ( 10 , 10 ) a ( i , 1 : n ) ! часть одной строки a ( 1 : m , j ) ! часть одного столбца a ( i , : ) ! вся строка a ( i , 1 : n : 3 ) ! каждый третий элемент строки a ( i , 10 : 1 : - 1 ) ! строка в обратном порядке a ( ( / 1 , 7 , 3 , 2 / ), 1 ) ! векторный индекс a ( 1 , 2 : 11 : 2 ) ! 11 допустимо, так как не ссылается a ( :, 1 : 7 ) ! раздел ранга два
Обратите внимание, что векторный индекс с повторяющимися значениями не может появляться в левой части присваивания, так как это будет неоднозначно. Таким образом,
б ( ( / 1 , 7 , 3 , 7 / ) ) = ( / 1 , 2 , 3 , 4 / )
недопустимо. Также раздел с индексом вектора не должен быть предоставлен как фактический аргумент для аргумента- пустышки OUT
или INOUT
фиктивного аргумента. Массивы массивов не допускаются:
tar % du ! незаконный
Отметим, что на заданное значение в массиве можно ссылаться и как на элемент, и как на раздел:
a ( 1 , 1 ) ! скаляр (ранг ноль) a ( 1 : 1 , 1 ) ! раздел массива (ранг один)
в зависимости от обстоятельств или требований. Квалифицируя объекты производного типа, мы получаем элементы или разделы в зависимости от правила, указанного ранее:
tar % u ! раздел массива (компонент структуры) tar ( 1 , 1 )% u ! компонент элемента массива
Умножение векторов и матриц
DOT_PRODUCT | Скалярное произведение двух массивов ранга 1 |
MATMUL | Умножение матриц |
Сокращение массива
ALL | Истина, если все значения истинны |
ANY | True, если любое значение является true. Пример:IF (ANY( a > b)) THEN |
COUNT | Количество истинных элементов в массиве |
MAXVAL | Максимальное значение в массиве |
MINVAL | Минимальное значение в массиве |
PRODUCT | Произведение элементов массива |
SUM | Сумма элементов массива |
Запрос массива
ALLOCATED | Статус распределения массива |
LBOUND | Нижние границы размерности массива |
SHAPE | Форма массива (или скаляра) |
SIZE | Общее количество элементов в массиве |
UBOUND | Верхние границы размерности массива |
Построение массива
MERGE | Объединить под маской |
PACK | Упаковать массив в массив ранга один под маской |
SPREAD | Скопировать массив, добавив измерение |
UNPACK | Распаковываем массив ранга один в массив под маской |
Изменение формы массива
RESHAPE | Изменить форму массива |
Манипуляции с массивами
CSHIFT | Круговой сдвиг |
EOSHIFT | Конец смены |
TRANSPOSE | Транспонирование массива ранга два |
Расположение массива
MAXLOC | Расположение первого максимального значения в массиве |
MINLOC | Расположение первого минимального значения в массиве |
Указатели — это переменные с POINTER
атрибутом ; они не являются отдельным типом данных (и поэтому никакая «арифметика указателей» невозможна).
РЕАЛЬНЫЙ , УКАЗАТЕЛЬ :: var
Они концептуально являются дескриптором, перечисляющим атрибуты объектов (целей), на которые может указывать указатель, и адрес цели, если таковой имеется. Они не имеют ассоциированного хранилища, пока оно не будет выделено или иным образом ассоциировано (путем назначения указателя, см. ниже):
ВЫДЕЛИТЬ ( var )
и они автоматически разыменовываются, поэтому не требуется никаких специальных символов. В
вар = вар + 2,3
используется и изменяется значение цели var. Указатели не могут передаваться через ввод-вывод. Оператор
ЗАПИСЬ * , вар
записывает значение цели var, а не сам дескриптор указателя.
Указатель может указывать на другой указатель и, следовательно, на свою цель или на статический объект, имеющий TARGET
атрибут:
REAL , POINTER :: object REAL , TARGET :: target_obj var => object ! назначение указателя var => target_obj
но они строго типизированы:
ЦЕЛОЕ ЧИСЛО , УКАЗАТЕЛЬ :: int_var var => int_var ! незаконно - типы должны совпадать
и, аналогично, для массивов должны совпадать как ранги, так и тип.
Указатель может быть компонентом производного типа:
ТИП запись ! тип для разреженной матрицы REAL :: значение INTEGER :: индекс ТИП ( запись ), POINTER :: следующий ! примечание рекурсия КОНЕЦ ТИП запись
и мы можем определить начало связанной цепочки таких записей:
ТИП ( запись ), УКАЗАТЕЛЬ :: цепь
После соответствующих распределений и определений первые две записи можно рассматривать как
цепочка % цепочка создания стоимости % следующая % цепочка создания стоимости % индекс цепочка % следующая % индекс цепочка % следующая цепочка % следующая % следующая
но мы обычно определяем дополнительные указатели, указывающие, например, на первую и текущую записи в списке.
Статус ассоциации указателя — один из
DEALLOCATE ( p , q ) ! для возврата хранилища NULLIFY ( p , q ) ! для установки в 'null'
Необходимо соблюдать осторожность, чтобы не оставить указатель «висящим» при использовании DEALLOCATE
его цели, не аннулировав при этом любой другой указатель, ссылающийся на него.
Встроенная функция ASSOCIATED
может проверить статус ассоциации определенного указателя:
ЕСЛИ ( АССОЦИИРОВАННЫЙ ( ptr )) ТОГДА
или между определенным указателем и определенной целью (которая сама может быть указателем):
ЕСЛИ ( АССОЦИИРОВАННЫЙ ( ptr , target )) ТОГДА
Альтернативным способом инициализации указателя, также в операторе спецификации, является использование NULL
функции:
REAL , POINTER , DIMENSION (:) :: vector => NULL () ! время компиляции vector => NULL () ! время выполнения
Для внутренних типов мы можем 'прочесывать' указатели по разным наборам целевых данных, используя тот же код без какого-либо перемещения данных. Учитывая манипуляцию матрицей y = BC z , мы можем написать следующий код (хотя в этом случае тот же результат можно было бы получить проще другими способами):
REAL , TARGET :: b ( 10 , 10 ), c ( 10 , 10 ), r ( 10 ), s ( 10 ), z ( 10 ) REAL , POINTER :: a (:,:), x (:), y (:) INTEGER mult : DO mult = 1 , 2 IF ( mult == 1 ) THEN y => r ! нет перемещения данных a => c x => z ELSE y => s ! нет перемещения данных a => b x => r END IF y = MATMUL ( a , x ) ! общее вычисление END DO
Для объектов производного типа мы должны различать указатель и обычное присваивание.
ТИП ( запись ), УКАЗАТЕЛЬ :: первый , текущий : первый => текущий
назначение заставляет сначала указывать на ток, тогда как
первый = текущий
заставляет текущий перезаписывать первым и эквивалентно
первый % значение = текущий % значение первый % индекс = текущий % индекс первый % следующий => текущий % следующий
Если фактический аргумент является указателем, то, если фиктивный аргумент также является указателем,
INTENT
атрибута (это было бы неоднозначно),Если фиктивный аргумент не является указателем, он становится связанным с целью фактического аргумента:
REAL , УКАЗАТЕЛЬ :: a (:,:) : ВЫДЕЛИТЬ ( a ( 80 , 80 )) : ВЫЗОВ sub ( a ) : ПОДПРОГРАММА sub ( c ) REAL c (:, :)
Результаты функции также могут иметь POINTER
атрибут; это полезно, если размер результата зависит от вычислений, выполняемых в функции, как в
ИСПОЛЬЗОВАНИЕ data_handler REAL x ( 100 ) REAL , УКАЗАТЕЛЬ :: y (:) : y => compact ( x )
где модуль data_handler содержит
FUNCTION compact ( x ) REAL , POINTER :: compact (:) REAL x (:) ! Процедура удаления дубликатов из массива x INTEGER n : ! Найти количество различных значений, n ALLOCATE ( compact ( n )) : ! Скопировать различные значения в compact END FUNCTION compact
Результат можно использовать в выражении (но он должен быть связан с определенной целью).
Они не существуют как таковые: учитывая
ТИП ( запись ) :: строки ( n )
затем
строки % следующие ! нелегально
был бы таким объектом, но с нерегулярным шаблоном хранения. По этой причине они не допускаются. Однако мы можем достичь того же эффекта, определив производный тип данных с указателем в качестве его единственного компонента:
ТИП строка REAL , УКАЗАТЕЛЬ :: r (:) КОНЕЦ ТИП
и затем определение массивов этого типа данных
ТИП ( строка ) :: s ( n ), t ( n )
где хранилище для строк может быть выделено, например,
DO i = 1 , n ALLOCATE ( t ( i )% r ( 1 : i )) ! Выделить строку i длины i END DO
Присвоение массива тогда эквивалентно присвоению указателей для всех компонентов.s = t
s(i)%r => t(i)%r
Дан массив
РЕАЛЬНЫЙ , ЦЕЛЕВОЙ :: таблица ( 100 , 100 )
на который часто ссылаются с фиксированными индексами
таблица ( м : н , п : д )
эти ссылки могут быть заменены на
REAL , РАЗМЕРНОСТЬ (:, :), УКАЗАТЕЛЬ :: окно : окно => таблица ( m : n , p : q )
Индексы окна — это . Аналогично, для
(как уже определено в ), мы можем использовать, скажем, для указания на все компоненты u tar и индексировать его как1:n-m+1, 1:q-p+1
tar%u
taru => tar%u
taru(1, 2)
Подстрочные индексы такие же, как у самого tar. (Это заменяет еще больше EQUIVALENCE
.)
В ассоциации указателей
указатель => выражение_массива
нижние границы для pointer
определяются так, как если бы lbound
было применено к array_expression
. Таким образом, когда указатель назначается всей переменной массива, он наследует нижние границы переменной, в противном случае нижние границы по умолчанию равны 1.
Fortran 2003 позволяет указывать произвольные нижние границы для ассоциации указателей, например
окно ( r :, s :) => таблица ( m : n , p : q )
так что границы window
становятся r:r+n-m,s:s+q-p
. В Fortran 95 эта функция отсутствует; однако ее можно смоделировать с помощью следующего трюка (основанного на правилах ассоциации указателей для предполагаемых фиктивных аргументов массива форм):
ФУНКЦИЯ remap_bounds2 ( lb1 , lb2 , массив ) РЕЗУЛЬТАТ ( ptr ) ЦЕЛОЕ ЧИСЛО , НАМЕРЕНИЕ ( IN ) :: lb1 , lb2 ДЕЙСТВИТЕЛЬНОЕ , РАЗМЕР ( lb1 :, lb2 :), НАМЕРЕНИЕ ( IN ), ЦЕЛЬ :: массив ДЕЙСТВИТЕЛЬНОЕ , РАЗМЕР (:,:), УКАЗАТЕЛЬ :: ptr ptr => массив КОНЕЦ ФУНКЦИИ : окно => remap_bounds2 ( r , s , таблица ( m : n , p : q ))
Исходный код расширенного примера использования указателей для поддержки структуры данных находится в pointer.f90.
Большинство встроенных функций уже упоминались. Здесь мы рассмотрим только их общую классификацию и те, которые до сих пор были опущены. Все встроенные процедуры могут использоваться с ключевыми аргументами:
ВЫЗОВ ДАТА_И_ВРЕМЯ ( ВРЕМЯ = t )
и многие из них имеют необязательные аргументы.
Внутренние процедуры сгруппированы в четыре категории:
ABS(a)
;PRECISION(a)
;RESHAPE(a, b)
;SYSTEM_CLOCK
.Процедуры, которые еще не введены, следующие:
Запрос бита
BIT_SIZE | Количество бит в модели |
Манипуляция битами
BTEST | Тестирование бит |
IAND | Логическое И |
IBCLR | Очистить бит |
IBITS | Извлечение бита |
IBSET | Установить бит |
IEOR | Исключительное ИЛИ |
IOR | Инклюзивное ИЛИ |
ISHFT | Логический сдвиг |
ISHFTC | Круговой сдвиг |
NOT | Логическое дополнение |
Передаточная функция, как в
ЦЕЛОЕ ЧИСЛО :: i = ПЕРЕДАЧА ( 'abcd' , 0 )
(заменяет часть ЭКВИВАЛЕНТНОСТИ)
Подпрограммы
DATE_AND_TIME | Получить дату и/или время |
MVBITS | Копирует биты |
RANDOM_NUMBER | Возвращает псевдослучайные числа |
RANDOM_SEED | Доступ к семенам |
SYSTEM_CLOCK | Доступ к системным часам |
CPU_TIME | Возвращает процессорное время в секундах |
Эти примеры иллюстрируют различные формы списков ввода-вывода с некоторыми простыми форматами (см. ниже):
ЦЕЛОЕ ЧИСЛО :: i ДЕЙСТВИТЕЛЬНОЕ , РАЗМЕРНОСТЬ ( 10 ) :: a СИМВОЛ ( длина = 20 ) :: слово ПЕЧАТЬ "(i10)" , i ПЕЧАТЬ "(10f10.3)" , a ПЕЧАТЬ "(3f10.3)" , a ( 1 ), a ( 2 ), a ( 3 ) ПЕЧАТЬ "(a10)" , слово ( 5 : 14 ) ПЕЧАТЬ "(3f10.3)" , a ( 1 ) * a ( 2 ) + i , КОРЕНЬ ( a ( 3 : 4 ))
Переменные, но не выражения, одинаково допустимы во входных операторах, использующих READ
оператор:
ЧИТАЙТЕ "(i10)" , я
Если массив отображается как элемент, он обрабатывается так, как если бы элементы были указаны в порядке элементов массива.
Все указатели в списке ввода-вывода должны быть связаны с целью, а передача происходит между файлом и целями.
Элемент производного типа рассматривается так, как если бы компоненты были указаны в том же порядке, что и в объявлении типа, поэтому
читать "(8f10.5)" , p , t ! типы точка и треугольник
имеет тот же эффект, что и утверждение
ЧИТАЙТЕ "(8f10.5)" , p % x , p % y , t % a % x , t % a % y , t % b % x , & t % b % y , t % c % x , t % c % y
Объект в списке ввода-вывода не может иметь производный тип, имеющий компонент указателя на любом уровне выбора компонента.
Обратите внимание, что массив нулевого размера может встречаться как элемент в списке ввода-вывода. Такой элемент не соответствует фактической передаче данных.
Спецификация формата может быть также задана в виде символьного выражения:
СИМВОЛ ( len =* ), параметр :: form = "(f10.3)" : PRINT form , q
или как звездочка – это тип ввода-вывода, известный как ввод-вывод , управляемый списком (см. ниже), в котором формат определяется компьютерной системой:
PRINT * , "Квадратный корень из q = " , SQRT ( q )
Операции ввода/вывода используются для передачи данных между хранилищем исполняемой программы и внешним носителем, указанным номером устройства . Однако два оператора ввода/вывода PRINT
и вариант READ
не ссылаются ни на один номер устройства: это называется терминальным вводом/выводом. В противном случае форма будет следующей:
ЧИТАТЬ ( ЕДИНИЦА = 4 , FMT = "(f10.3)" ) q ЧИТАТЬ ( ЕДИНИЦА = nunit , FMT = "(f10.3)" ) q ЧИТАТЬ ( ЕДИНИЦА = 4 * i + j , FMT = "(f10.3)" ) a
где UNIT=
необязательно. Значение может быть любым неотрицательным целым числом, разрешенным системой для этой цели (но 0, 5 и 6 часто обозначают ошибку, клавиатуру и терминал соответственно).
Звездочка — это вариант — опять же с клавиатуры:
ЧИТАТЬ ( ЕДИНИЦА =* , FMT = "(f10.3)" ) q
Чтение с указателем единицы измерения позволяет обрабатывать исключения :
READ ( UNIT = NUNIT , FMT = "(3f10.3)" , IOSTAT = ios ) a , b , c IF ( ios == 0 ) THEN ! Успешное чтение - продолжить выполнение. : ELSE ! Состояние ошибки - предпринять соответствующие действия. CALL error ( ios ) END IF
Существует второй тип форматированного выходного оператора WRITE
:
ЗАПИСЬ ( ЕДИНИЦА = nout , FMT = "(10f10.3)" , IOSTAT = ios ) а
Они позволяют программе выполнять преобразование форматов между различными представлениями в области хранения, определенной внутри самой программы.
ЦЕЛОЕ ЧИСЛО , РАЗМЕР ( 30 ) :: ival ЦЕЛОЕ ЧИСЛО :: key СИМВОЛ ( ДЛИНА = 30 ) :: буфер СИМВОЛ ( ДЛИНА = 6 ), РАЗМЕР ( 3 ), ПАРАМЕТР :: form = ( / "(30i1)" , "(15i2)" , "(10i3)" / ) READ ( ЕДИНИЦА =* , FMT = "(a30,i1)" ) буфер , key READ ( ЕДИНИЦА = буфер , FMT = form ( ключ )) ival ( 1 : 30 / ключ )
Если внутренний файл является скаляром, он имеет одну запись, длина которой равна длине скаляра.
Если это массив, его элементы, в порядке следования элементов массива, рассматриваются как последовательные записи файла, и каждая имеет длину, равную длине элемента массива.
Пример использования WRITE
утверждения:
INTEGER :: день REAL :: наличные CHARACTER ( LEN = 50 ) :: строка : ! записать в строку WRITE ( UNIT = строка , FMT = "(a, i2, a, f8.2, a)" ) "Выручка за день " , день , " составляет " , наличные , " долларов"
которые могли бы написать
Выручка за 3-й день составила 4329,15 долларов.
Пример чтения без указанного формата для ввода:
ЦЕЛОЕ ЧИСЛО :: i ДЕЙСТВИТЕЛЬНОЕ :: a КОМПЛЕКСНОЕ , РАЗМЕРНОСТЬ ( 2 ) :: поле ЛОГИЧЕСКОЕ :: флаг СИМВОЛ ( ДЛИНА = 12 ) :: заголовок СИМВОЛ ( ДЛИНА = 4 ) :: слово : ЧИТАТЬ * , i , a , поле , флаг , заголовок , слово
Если это считывает входную запись
10 6,4 ( 1,0 , 0,0 ) ( 2,0 , 0,0 ) t тест /
(в котором в качестве разделителей используются пробелы), то i
, a
, field
, flag
, и title
приобретут значения 10, 6,4, (1,0,0,0) и (2,0,0,0) .true.
и test
соответственно, а word
останется неизменным.
Кавычки или апострофы необходимы в качестве разделителей для строки, содержащей пробел.
Это форма чтения и записи без постоянного продвижения позиции файла вперед следующей записи. В то время как продвигающийся оператор ввода-вывода всегда перемещает файл после последней записи, к которой был получен доступ, непродвигающийся оператор ввода-вывода не выполняет такого перемещения и, следовательно, может оставить файл позиционированным внутри записи.
CHARACTER ( LEN = 3 ) :: key INTEGER :: u , s , ios : READ ( UNIT = u , FMT = "(a3)" , ADVANCE = "no" , SIZE = s , IOSTAT = ios ) key IF ( ios == 0 ) THEN : ELSE ! key не находится ни в одной записи key ( s + 1 :) = "" : END IF
При непродвижении можно прочитать первые несколько символов записи, а при обычном чтении — оставшуюся часть.
Чтобы вывести приглашение на экран терминала и прочитать его со следующей позиции символа на экране без промежуточного перевода строки, мы можем написать:
WRITE ( UNIT =* , FMT = "(a)" , ADVANCE = "no" ) "введите следующее простое число:" READ ( UNIT =* , FMT = "(i10)" ) простое_число
Непрогрессирующий ввод-вывод предназначен для внешних файлов и недоступен для ввода-вывода, управляемого списком.
Можно указать, что дескриптор редактирования должен повторяться указанное количество раз, используя счетчик повторов :10f12.3
Дескриптор редактирования с косой чертой (см. ниже) может иметь количество повторений, и количество повторений может также применяться к группе дескрипторов редактирования, заключенных в скобки, с вложением:
ПЕЧАТЬ "(2(2i5,2f8.2))" , я ( 1 ), я ( 2 ), а ( 1 ) , а ( 2 ) , я ( 3 ) , я ( 4 ) , а ( 3 ), а ( 4 )
Все спецификации формата могут быть повторены:
ПЕЧАТЬ "(10i8)" , ( / ( i ( j ), j = 1 , 200 ) / )
записывает 10 целых чисел, каждое из которых занимает 8 позиций символов, на каждой из 20 строк (повторение спецификации формата приводит к переходу на следующую строку).
iW iW.M
fW.D esW.D esW.DeE
f
или es
дескрипторы редактирования lW
a aW
ТИП , PUBLIC :: строка ЦЕЛОЕ ЧИСЛО :: длина СИМВОЛ ( ДЛИНА = 20 ) :: слово КОНЕЦ ТИП строка ТИП ( строка ) :: текст ЧИТАТЬ ( ЕДИНИЦА =* , FMT = "(i2, a)" ) текст
Условия настройки дескрипторов редактирования управления :
ss
редактирования (sign suppress) подавляет начальные знаки плюс. Для включения печати знака плюс sp
используется дескриптор (sign print). s
Дескриптор редактирования восстанавливает опцию для процессора.Управление дескрипторами редактирования для немедленной обработки :
tN trN tlN
ЧИТАТЬ ( ЕДИНИЦА =* , FMT = "(t3,i4, tl4,i1, i2)" ) i , j , k
/ N/
ЧИТАЙТЕ "(i5,i3,/,i5,i3,i2 ) " , я , к , л , м
Обратите внимание, что
ПЕЧАТЬ "(i5,4/,i5)" , i , j
:
завершает управление форматом, если в списке ввода-вывода больше нет элементов.ПЕЧАТЬ "( i5, :, /, i5, :, /, i5)" , ( / ( l ( i ), i = 1 , n ) / )
n
равно 1 или 2.Этот тип ввода-вывода следует использовать только в тех случаях, когда записи генерируются программой на одном компьютере и считываются на том же компьютере или другом компьютере с использованием тех же внутренних числовых представлений:
ОТКРЫТЬ ( ЕДИНИЦА = 4 , ФАЙЛ = 'test' , ФОРМА = 'неформатированный' ) ЧТЕНИЕ ( ЕДИНИЦА = 4 ) q ЗАПИСЬ ( ЕДИНИЦА = nout , IOSTAT = ios ) a !no fmt=
Эта форма ввода-вывода также известна как произвольный доступ или индексированный ввод-вывод. Здесь все записи имеют одинаковую длину, и каждая запись идентифицируется индексным номером. Можно записывать, читать или перезаписывать любую указанную запись независимо от ее положения.
INTEGER , PARAMETER :: nunit = 2 , length = 100 REAL , DIMENSION ( length ) :: a REAL , DIMENSION ( length + 1 : 2 * length ) :: b INTEGER :: i , rec_length : INQUIRE ( IOLENGTH = rec_length ) a OPEN ( UNIT = nunit , ACCESS = "direct" , RECL = rec_length , STATUS = "scratch" , ACTION = "readwrite" ) : ! Записать массив b в файл прямого доступа в запись 14 WRITE ( UNIT = nunit , REC = 14 ) b : ! ! Считать массив обратно в массив a READ ( UNIT = nunit , REC = 14 ) a : DO i = 1 , length / 2 a ( i ) = i END DO ! ! Заменить измененную запись WRITE ( UNIT = nunit , REC = 14 ) a
Файл должен быть внешним, а форматирование по списку и непоследовательный ввод-вывод недоступны.
Еще раз напоминаем, что это всего лишь обзор.
BACKSPACE
:BACKSPACE ( UNIT = u [, IOSTAT = ios ]) ! где [ ] означает необязательно
REWIND
:ПЕРЕМАТКА НАЗАД ( ЕДИНИЦА = u [, IOSTAT = ios ])
endfile
:КОНЕЦФАЙЛА ( единица измерения = u [, iostat = ios ])
OPEN
Оператор используется для подключения внешнего файла к устройству, создания файла, который предварительно подключен, или создания файла и подключения его к устройству. Синтаксис:
ОТКРЫТЫЙ ( ЕДИНИЦА = u , СТАТУС = st , ДЕЙСТВИЕ = act [, olist ])
где olist
— список необязательных спецификаторов. Спецификаторы могут появляться в любом порядке.
ОТКРЫТО ( ЕДИНИЦА = 2 , IOSTAT = ios , ФАЙЛ = "cities" , СТАТУС = "новый" , ДОСТУП = "прямой" , & ДЕЙСТВИЕ = "чтение/запись" , ЗАПИСЬ = 100 )
Другими спецификаторами являются FORM
и POSITION
.
CLOSE
Используется для отключения файла от устройства.
ЗАКРЫТЬ ( ЕДИНИЦА = u [, IOSTAT = ios ] [, СТАТУС = st ])
как в
ЗАКРЫТЬ ( ЕДИНИЦА = 2 , IOSTAT = ios , СТАТУС = "удалить" )
inquire
В любой момент выполнения программы можно запросить информацию о состоянии и атрибутах файла, используя этот оператор.
Используя вариант этого утверждения, можно аналогичным образом определить статус устройства, например, существует ли номер устройства для этой системы.
Другой вариант позволяет запрашивать длину выходного списка при использовании для записи неформатированной записи.
Для запроса по единице
ЗАПРОС ( ЕДИНИЦА = u , ilist )
или для запроса по файлу
ЗАПРОС ( ФАЙЛ = fln , ilist )
или для запроса по списку ввода/вывода
INQUIRE ( IOLENGTH = длина ) olist
В качестве примера
ЛОГИЧЕСКИЙ :: ex , op СИМВОЛ ( LEN = 11 ) :: nam , acc , seq , frm ЦЕЛОЕ ЧИСЛО :: irc , nr ЗАПРОС ( ЕДИНИЦА = 2 , EXIST = ex , OPENED = op , NAME = nam , ACCESS = acc , SEQUENTIAL = seq , & FORM = frm , RECL = irc , NEXTREC = nr )
урожайность
ex . true . op . true . nam cities acc ПРЯМАЯ seq NO frm НЕФОРМАТИРОВАННЫЙ irec 100 nr 1
(при условии отсутствия промежуточных операций чтения или записи).
Другие спецификаторы: IOSTAT, OPENED, NUMBER, NAMED, FORMATTED, POSITION, ACTION, READ, WRITE, READWRITE
.