Fortran
Современные альтернативы историческим особенностям
Поиск…
Неявные типы переменных
Когда Fortran была первоначально разработана, память была в отличной форме. Имена переменных и процедур могут содержать не более 6 символов, а переменные часто неявно печатаются . Это означает, что первая буква имени переменной определяет ее тип.
- переменные, начинающиеся с i, j, ..., n, являются
integer
- все остальные (a, b, ..., h, o, p, ..., z) являются
real
Возможны такие программы, как Fortran:
program badbadnotgood
j = 4
key = 5 ! only the first letter determines the type
x = 3.142
print*, "j = ", j, "key = ", key, "x = ", x
end program badbadnotgood
Вы даже можете определить свои собственные неявные правила с implicit
выражением:
! all variables are real by default
implicit real (a-z)
или же
! variables starting with x, y, z are complex
! variables starting with c, s are character with length of 4 bytes
! and all other letters have their default implicit type
implicit complex (x,y,z), character*4 (c,s)
Неявная типизация больше не считается лучшей практикой. Очень легко сделать ошибку, используя неявное типирование, поскольку опечатки могут остаться незамеченными, например
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
Эта программа будет успешно работать и делать не то.
Чтобы отключить неявное типирование, можно использовать implicit none
оператор.
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
Если мы не использовали implicit none
в программе oops
выше, компилятор заметил бы сразу, и возникает ошибка.
Арифметическое утверждение if
Арифметический оператор if
позволяет использовать три ветви в зависимости от результата арифметического выражения
if (arith_expr) label1, label2, label3
Этот оператор if
переносит поток управления на одну из меток кода. Если результатом arith_expr
является отрицательная label1
, то если результат равен нулю, используется label2
, и если результат положительный, применяется последняя label3
. Арифметика, if
требует всех трех меток, но допускает повторное использование меток, поэтому это утверждение можно упростить до двух ветвей if
.
Примеры:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
Теперь эта функция устарела с той же функциональностью, предлагаемых в , if
заявление и if-else
конструкция. Например, фрагмент
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
может быть записана как конструкция if-else
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
Замена оператора if
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
может быть
if (X<=0) print*, "Negative or zero"
Неблокирующие конструкторы DO
Конструкция без блока do
выглядит так:
integer i
do 100, i=1, 5
100 print *, i
То есть, если заявленный оператор терминации не является оператором continue
. Существуют различные ограничения на утверждение, которое может использоваться как заявление о завершении, и все это, как правило, очень запутанно.
Такая неблокирующая конструкция может быть переписана в виде блока в виде
integer i
do 100 i=1,5
print *, i
100 continue
или лучше, используя end do
терминации заявление,
integer i
do i=1,5
print *, i
end do
Альтернативный возврат
Альтернативный возврат - это средство контроля потока выполнения при возврате из подпрограммы. Он часто используется как форма обработки ошибок:
real x
call sub(x, 1, *100, *200)
print*, "Success:", x
stop
100 print*, "Negative input value"
stop
200 print*, "Input value too large"
stop
end
subroutine sub(x, i, *, *)
real, intent(out) :: x
integer, intent(in) :: i
if (i<0) return 1
if (i>10) return 2
x = i
end subroutine
Альтернативный возврат отмечен аргументами *
в списке аргументов подпрограммы.
В call
заявлении выше *100
и *200
см операторов , помеченных 100
и 200
соответственно.
В самой подпрограмме операторы return
соответствующие альтернативному возврату, имеют число. Это число не является возвращаемым значением, но обозначает предоставленную метку, которой выполняется исполнение при возврате. В этом случае return 1
пропускает выполнение в оператор с меткой 100
и return 2
прохождение выполнения в оператор, помеченный знаком 200
. Унаследованный оператор return
или завершение выполнения подпрограммы без оператора return
, выполнение пассессии сразу после оператора вызова.
Синтаксис альтернативного возврата сильно отличается от других форм ассоциации аргументов, и средство вводит управление потоком вопреки современным вкусам. Более приятное управление потоком можно управлять с возвратом целочисленного кода состояния.
real x
integer status
call sub(x, 1, status)
select case (status)
case (0)
print*, "Success:", x
case (1)
print*, "Negative input value"
case (2)
print*, "Input value too large"
end select
end
subroutine sub(x, i, status)
real, intent(out) :: x
integer, intent(in) :: i
integer, intent(out) :: status
status = 0
if (i<0) then
status = 1
else if (i>10)
status = 2
else
x = i
end if
end subroutine
Фиксированная исходная форма
Первоначально Fortran был разработан для формата фиксированного формата на основе 80-битной перфокарты:
Да. Это строка собственного кода автора.
Они были созданы на перфокартной машине, примерно так:
Изображения - это оригинальная фотография автора
Формат, как показано на иллюстрированной образце карты, имел первые пять столбцов, зарезервированных для ярлыков операторов. Первый столбец использовался для обозначения комментариев буквой C. Шестой столбец использовался для обозначения продолжения оператора (путем вставки любого символа, отличного от нуля «0»). Последние 8 столбцов были использованы для идентификации и секвенирования карты, что было очень полезно, если вы уронили свою колоду карт на пол! Кодировка символов для перфокарт имела только ограниченный набор символов и была только в верхнем регистре. В результате программы Fortran выглядели следующим образом:
DIMENSION A(10) 00000001
C THIS IS A COMMENT STATEMENT TO EXPLAIN THIS EXAMPLE PROGRAM 00000002
WRITE (6,100) 00000003
100 FORMAT(169HTHIS IS A RATHER LONG STRING BEING OUTPUT WHICH GOES OVE00000004
1R MORE THAN ONE LINE, AND USES THE STATEMENT CONTINUATION MARKER IN00000005
2COLUMN 6, AND ALSO USES HOLLERITH STRING FORMAT) 00000006
STOP 00000007
END 00000008
Косвенный символ также игнорировался везде, за исключением внутренней константы символа Холлерита (как показано выше). Это означало, что пробелы могут возникать внутри зарезервированных слов и констант или полностью пропущены. Это имело побочный эффект некоторых довольно вводящих в заблуждение заявлений, таких как:
DO 1 I = 1.0
является присвоением переменной DO1I
тогда как:
DO1I = 1,0
фактически является циклом DO
для переменной I
Современный Fortran теперь не требует этой фиксированной формы ввода и разрешает свободную форму с использованием любых столбцов. Комментарии теперь обозначаются a !
который также может быть добавлен к строке оператора. Пробелы теперь не разрешены нигде и должны использоваться как разделители, как и на большинстве других языков. Вышеупомянутая программа может быть написана в современном Fortran как:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH no longer GOES OVER MORE THAN ONE LINE, AND does not need to USE THE STATEMENT CONTINUATION MARKER IN COLUMN 6, or the HOLLERITH STRING FORMAT"
Хотя продолжение старого стиля больше не используется, приведенный выше пример иллюстрирует, что очень длинные утверждения все равно будут возникать. Современный Фортран использует символ &
в конце и в начале продолжения. Например, мы могли бы написать выше в более читаемой форме:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH still &
&GOES OVER MORE THAN ONE LINE, AND does need to USE THE STATEMENT &
&CONTINUATION notation"
Общие блоки
В ранних формах Fortran единственным механизмом создания глобального хранилища переменных, видимым из подпрограмм и функций, является использование механизма блока COMMON
. Это разрешает последовательности переменных быть именами и совместно использоваться.
В дополнение к названным общим блокам может также быть пустой (неназванный) общий блок.
Можно было бы объявить пустой общий блок
common i, j
тогда как именованные блок- variables
могут быть объявлены как
common /variables/ i, j
В качестве полного примера мы могли бы представить хранилище кучи, которое используется подпрограммами, которые могут добавлять и удалять значения:
PROGRAM STACKING
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = 0
READ *, IVAL
CALL PUSH(IVAL)
CALL POP(IVAL)
END
SUBROUTINE PUSH(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = ICOUNT + 1
ISTACK(ICOUNT) = IVAL
RETURN
END
SUBROUTINE POP(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
IVAL = ISTACK(ICOUNT)
ICOUNT = ICOUNT - 1
RETURN
END
Общие утверждения могут использоваться для неявного объявления типа переменной и указания атрибута dimension
. Такое поведение часто является достаточным источником путаницы. Кроме того, подразумеваемая ассоциация хранения и требования для повторных определений между программными единицами используют использование обычных блоков, подверженных ошибкам.
Наконец, общие блоки очень ограничены в объектах, которые они содержат. Например, массив в общем блоке должен иметь явный размер; выделяемые объекты могут не возникнуть; производные типы не должны иметь инициализацию по умолчанию.
В современном Fortran это совместное использование переменных может быть связано с использованием модулей . Вышеприведенный пример может быть записан как:
module heap
implicit none
! In Fortran 2008 all module variables are implicitly saved
integer, save :: count = 0
integer, save :: stack(1023)
end module heap
program stacking
implicit none
integer val
read *, val
call push(val)
call pop(val)
contains
subroutine push(val)
use heap, only : count, stack
integer val
count = count + 1
stack(count) = val
end subroutine push
subroutine pop(val)
use heap, only : count, stack
integer val
val = stack(count)
count = count - 1
end subroutine pop
end program stacking
Именованные и пустые общие блоки имеют несколько другое поведение. Отметить:
- объекты в названных общих блоках могут быть определены изначально; объекты в пробе не должны быть
- объекты в пустых общих блоках ведут себя так, как будто общий блок имеет атрибут
save
; объекты в именованных общих блоках без атрибутаsave
могут стать неопределенными, когда блок не входит в область активного программного блока
Эта последняя точка может быть противопоставлена поведению модульных переменных в современном коде. Все переменные модуля в Fortran 2008 неявно сохраняются и не становятся неопределенными, когда модуль выходит из области видимости. До того, как переменные модуля Fortran 2008, как и переменные в именованных общих блоках, также станут неопределенными, когда модуль выходит из сферы действия.
Назначенный GOTO
Назначенный GOTO использует целочисленную переменную, которой присваивается метка оператора с помощью оператора ASSIGN.
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
Назначенный GOTO устаревает в Fortran 90 и удаляется в Fortran 95 и более поздних версиях. Этого можно избежать в современном коде, используя процедуры, внутренние процедуры, указатели на процедуры и другие функции.
Вычисление GOTO
Вычисленный GOTO позволяет разветвлять программу в соответствии со значением целочисленного выражения.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
Если scalar-integer-expression
label_1
scalar-integer-expression
равно 1, программа продолжается на метке оператора label_1
, если она равна 2, она переходит к label_2
и т. Д. Если он меньше 1
или больше, чем n
программа продолжит следующую строку.
Пример:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
перейдем к метке оператора 20.
Эта форма goto
устарела в Fortran 95 и позже, будучи замененной конструкцией select case
.
Назначенные спецификаторы формата
До Fortran 95 можно было использовать назначенные форматы для ввода или вывода. Рассматривать
integer i, fmt
read *, i
assign 100 to fmt
if (i<100000) assign 200 to fmt
print fmt, i
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
assign
оператор присваивает метку заявление к целочисленной переменной. Эта целочисленная переменная позже будет использоваться в качестве спецификатора формата в заявлении print
.
Такое назначение спецификатора формата было удалено в Fortran 95. Вместо этого более современный код может использовать некоторую другую форму управления потоком выполнения
integer i
read *, i
if (i<100000) then
print 100, i
else
print 200, i
end if
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
или символьная переменная может использоваться как спецификатор формата
character(29), target :: big_fmt='("This is a big number", I10)'
character(30), target :: small_fmt='("This is a small number", I6)'
character(:), pointer :: fmt
integer i
read *, i
fmt=>big_fmt
if (i<100000) fmt=>small_fmt
print fmt, i
end
Функции выписки
Рассмотрите программу
implicit none
integer f, i
f(i)=i
print *, f(1)
end
Здесь f
- функция оператора. Он имеет целочисленный тип результата, принимающий один целочисленный аргумент фиктивного аргумента. 1
Такая функция оператора существует в пределах области, в которой она определена. В частности, он имеет доступ к переменным и именованным константам, доступным в этой области.
Тем не менее, функции оператора подвержены многим ограничениям и потенциально запутывают (смотря на случайный взгляд, как оператор присваивания элемента массива). Важными ограничениями являются:
- результат функции и фиктивные аргументы должны быть скалярными
- фиктивные аргументы находятся в той же области, что и функция
- функции оператора не имеют локальных переменных
- функции оператора не могут передаваться как фактические аргументы
Основные преимущества функций оператора повторяются внутренними функциями
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
Внутренние функции не подпадают под ограничения, упомянутые выше, хотя, возможно, стоит отметить, что внутренняя подпрограмма может не содержать дополнительной внутренней подпрограммы (но она может содержать функцию оператора).
Внутренние функции имеют свою собственную область действия, но также имеют доступную ассоциацию хостов.
1 В реальных старых примерах кода было бы необычно видеть, что фиктивные аргументы функции оператора неявно типизированы, даже если результат имеет явный тип.