Программирование
сложных задач
Термины
и Принятые сокращения
- Программа
состоит из Программных единиц (ПЕ), ПЕ – наименьшая
часть текста решения задачи, которую можно оформить как самостоятельный
текстовый файл, компилируемый, помещаемый в библиотеку
- Подпрограмма (ПП) - это Функция или Процедура
- Функция решает подзадачу с единственным результатом,
возвращая результат в формулу, где она упоминается
- Процедура решает подзадачу с любым количеством
результатов
- Модуль содержит описания глобальных объектов и
модульных ПП для их обработки
- Интерфейс или шаблон для вызова ПП указывает вид ПП
и ее имя, а также содержит перечень и описания формальных параметров
- Формальные параметры – это объекты вызываемой
подпрограммы, их пишут в заголовке ПП
- Фактические параметры (аргументы) – это объекты
вызывающей программы, их перечисляют при вызове ПП
- Данные передают в вызываемую подпрограмму,
сопоставляя аргументы формальным параметрам
- Результаты возвращают из процедуры, сопоставляя
формальные параметры аргументам
- Ассоциирование (присоединение) объектов носителя
в область видимости тех программ, которые в
нее вложены
- Ассоциирование (присоединение) глобальных объектов
Модуля в область видимости тех
программ, где модуль используется
- Область видимости объекта – это одна или много программ,
где над объектом можно выполнять действия
- Возможность
реализации принципа инкапсуляции – это
основное предназначение модуля
Аспекты
сложности задачи
Сложность имеет много аспектов: размер программы, организация данных и
интерфейсов, память и быстродействие, точность и т.д. Аспекта “большая
программа” коснемся в теме “подпрограммы
и функции”, аспекта организации данных - в теме “Типы данных“, новейших
интерфейсов и объема памяти - в теме “Программирование для WINDOWS”.
Во всех языках программирования имеются средства разделения задачи на
подзадачи, но они несколько различаются
- FORTRAN стандартные
функции, функции Ф90-внутренние и внешние, процедуры Ф90-внутренние и
внешние, а также модули –только в Ф90, причем модулям нет аналогов в
других языках
- PASCAL -
стандартные функции, функции, процедуры
- C-
стандартные функции, подпрограммы, они же могут быть и функциями
Опыт
программирования аккумулируется в библиотеках программ
- наиболее
трудоемкий сервис - это ввод-вывод, особенно вывод на экран - в DOS и
WINDOWS - это низкоуровневая графика - расширенный арсенал отображения
графической и текстовой информации на экране
- в
WINDOWS -это QuckWin - быстрый и безболезненный переход от отдельной
программы в DOS к приложению в многооконной многозадачной среде WINDOWS с
увеличением на 2 порядка объема доступной памяти и с более мощной
графикой на виртуальном экране.
- имеются
внеязыковые библиотеки, например, так реализован ввод-вывод в C и PASCAL
- имеются
внеязыковые библиотеки поддержки ОС, например, WinApi для Windows.
- имеются
графические библиотеки, в том числе низкоуровневая графика для “C”
и Фортран, а также внеязыковая реалистическая графика OPENGL
- имеется
уникальная библиотека программ на Фортране IMSL – более 1000 численных
методов, статистика, матричная алгебра (одинарная и двойная точность, в
комплексной плоскости) – все эти программы – это обычным образом
оформленные функции, процедуры, а также модули Ф90
- имеется
емкая библиотека программ на Фортране для IBM-PC фирмы
COMPAQ
- известны
компилятор и библиотека программ на Фортране и “C ” для IBM-PC фирмы
INTEL
Программы,
подпрограммы, процедуры, функции и модули
Простые,
например, учебные программы вовсе не требуют подпрограмм и модулей. Однако,
если в программе более 50-100 операторов, то человек, теряя контроль над
многими отдельными частями, путается. Чтобы исключить ошибки, задачу
подразделяют на подзадачи, программируя их в виде функций и процедур
(подпрограмм). Анализ решаемой задачи часто позволяет выделить в ней
повторяющиеся подзадачи. Оформив такую подзадачу в виде подпрограммы с
параметрами, можно ее многократно вызывать, варьируя параметры. Если все данные
сложной задачи передавать только через параметры, то их становится слишком
много. Альтернативный путь – заимствование данных у носителя или модуля – этот
путь реализован в Ф90. Часто повторяются и функции, которые надо вычислять, -
их тоже можно оформить в виде самостоятельных частей задачи. Нужны
подпрограммы и при смешанном программировании на разных языках, скажем
"С" и Фортран. Согласно принципу независимости подпрограмм каждую из
них можно компилировать самостоятельно, а результат компиляции поместить
в библиотеку. На эту личную библиотеку затем ссылаются при компоновке
наряду со стандартной библиотекой Фортрана. Принцип независимости программ
означает независимость обозначений и описаний, что позволяет в каждой решаемой
задачке, оформляемой как внешняя программа, полностью игнорировать обозначения
других программ. Наоборот, чтобы сохранить единый стиль и обозначения при
проектировании сложной программы, общие описания и определения выносят в
отдельный модуль в Фортране (include в языке “C”). Модули,
появившиеся в Ф90, куда более мощное средство, чем include.
Чтобы лучше понять отличия в оформлении программ
разных видов
· в учебном проекте
реальная задача подразделяется на подзадачи
· рассматриваются правила написания
программ и вспомогательных операторов
Учебный проект
Для
того чтобы войти в проблематику решим простенькую, но вполне реальную задачу.
На примере рассматриваются 3 основные вида программ.
Задача. Гражданам предлагаются разновидности Лото,
Спортлото, в которых на карточке из многих номеров предлагается отметить
несколько. В случае угаданных цифр сулят выигрыш, тем больший, чем больше цифр
предстоящего тиража угадано. Спрашивается, так ли велик шанс, выиграть?
Шанс - это вероятностная величина P, обратная числу вариантов S заполнения
карточки. Используем формулы
- P = 1 / S
- S = n! /
(m!(n-m)!) - число сочетаний из n по m
Попробуем
сначала написать более знакомую нам главную программу, задающую данные и
печатающую ответы. Для двух наиболее популярных игр "5 из 36" и
"6 из 45" рассчитаем шанс сорвать самый крутой куш. Остальные менее
значимые шансы оставим на самостоятельное программирование.
Главная программа. Каково число вариантов игр в лото "5 из 36"
и "6 из 45"
Обратим внимание на то, что
главная программа Loto_2_games
сама ничего не будет вычислять, а будет пользоваться услугами процедуры, названной Loto.
Две игры - два вызова процедуры Loto.
Процедура Loto. Для того чтобы
воспользоваться услугами процедуры Loto, - на это надо
обратить особое внимание - главной программе совсем не надо знать,
как внутри устроена подпрограмма. Что же надо знать?
Надо знать интерфейс
|
в нашем случае интерфейс ПП Loto
|
в каком виде оформлена ПП в виде процедуры или в виде
функции?
|
- подпрограмма
в виде процедуры
|
как называется процедура ?
|
Loto
|
Сколько у нее входных данных (входных параметров - как в математике)
и какого типа эти параметры?
|
- целое
n – сколько клеток на карточке
- целое
m-сколько клеток надо заполнить
на карточке
|
Сколько у нее выходных данных (выходных параметров) и
какого типа эти параметры ?
|
- вещественное
число - "вариантов заполнения карточки"
- вещественное
число - шанс (вероятность)
|
Если кто-то захочет
рассчитать другую игру
- Он не обязан знать, как
внутри устроена ПП Loto
- Но он должен знать
перечисленные сведения, объединенные понятием интерфейс ПП
- interface .. .. end interface –
это новый составной неисполняемый оператор Ф-90, в котором дан шаблон
вызова ПП
- придерживаясь этого шаблона
можно вызывать ПП и задавать параметры
- варьируя параметры ПП Loto, можно рассчитать сколько угодно игр
- расчеты производятся много
раз, но запрограммированы они ровно один раз в ПП Loto
Program Loto_2_games ! Программа Loto
-
расчет двух
игр "5 из 36" и "6 из
45"
real S36,P36, S45,P45
interface
Subroutine Loto ( m,n, S,P ) ! определение
подпрограммы Loto
integer,intent(in)
:: m,
n ! in - входные параметры
real,intent(out) :: S,P ! out - выходные параметры
end Subroutine Loto
end interface
open( 2, file='chance.txt' )
call Loto ( 5,36, S36,P36 ) !
вызов подпрограммы Loto - лото "5 из 36"
write(2,1) ‘5
из 36’, S36, P36
call Loto ( 6,45, S45,P45 ) !
вызов подпрограммы Loto - лото "6 из 45"
write(2,1) '6 из 45' , S45, P45'
1 format('
игра ‘,a,’ число вариантов: ',
g12.4, ' : шанс
с вероятностью = ' , g12.4)
end
С
учетом сделанных пояснений, процедура Loto может выглядеть так
Subroutine Loto ( m,n, S,P ) !
определение подпрограммы Loto
Integer,intent(in)
:: m,
n ! in - входные параметры
real,intent(out) ::
S,P ! out - выходные параметры
interface
Function
Factorial
( n ) ! определение интерфейса подпрограммы-функции
integer,intent(in)
:: n; real*8 Factorial
end Function Factorial
end
interface
S = Factorial (n) / (Factorial (m)*
Factorial (n-m)) ! троекратный вызов подпрограммы-функции
P = 1 / S
End
Разглядывая расчетную формулу для S,
можно обратить внимание на трехкратное использование в ней одной и той же
функции факториал - из этого заключим, что эту функцию стоит запрограммировать
однократно, а затем уже трижды ею воспользоваться.
Подпрограмма-функция Factoriial.
Для написания ПП Loto разработан и среди описаний ПП Loto записан
интерфейс функции
Factorial.
Надо знать интерфейс функции Factorial
|
в нашем случае
|
в каком виде оформлена подпрограмма в виде процедуры
или в виде функции?
|
подпрограмма в виде функции, один ответ-это сама
функция
|
как называется подпрограмма-функция
|
Factorial –
вещественное число удвоенной точности
|
Сколько у нее входных данных (в математике - аргументов) и
какого типа эти параметры ?
|
целое n - сколько
клеток на карточке
целое m - сколько
клеток надо заполнить
|
Примечания:
·
ответ у любой функции ровно один – в нашем примере - это и есть значение
функции Factorial,
которое после завершения ПП будет непосредственно использовано в формуле
вызывающей программы
·
Factorial - по смыслу задачи
целое число, но его придется описать как вещественное число удвоенной
точности из-за того, что факториал очень быстро растущая
величина, для справки
самое большое целое число integer порядка 1.e10
самое большое вещественное число
real порядка
1.е38
самое большое вещественное число
двойной точности real*8 порядка 1.е308
·
текст внутри оператора interface .. .. end interface, взятый из ПП Loto может служить заготовкой
для ПП-функции Factorial
·
этот текст, дополненный похожим допустимым способом вычисления
функции, может служить даже отладочным первоначальным вариантом ПП-функции,
который сгодится для отладки вызывающей программы
·
по форме вызов нашей собственной функции Factorial ничем не отличается от вызова стандартных функций
ПП-функция Factorial для вычисления
факториала n! = 1*2*3*.. *n может выглядеть так
Function Factorial ( n ) ! заголовок
подпрограммы-функции
real*8 Factorial ! тип результата двойной точности
integer,intent(in) :: n ! единственный
входной параметр
integer k
Factorial = 1
if ( n = = 0 ) return
! из
математики известен особый случай: 0!=1
do k=1,n
Factorial = Factorial *k
enddo
end
Function Factorial
Примечания:
· имя функции Factorial имеет
двойное назначение -
с одной стороны это общедоступное глобальное имя ПП-функции, для правильного вызова
ее из других ПЕ см. интерфейс
с другой стороны внутри ПП-функции это обычная переменная,
которой непременно должно быть присвоено значение рассчитываемого
факториала для передачи в вызывающую программу
· из математики известен
особый случай 0!=1 - в этом случае оператором return
досрочно завершается выполнение ПП с возвращением в вызывающую
программу
· функция Factorial вызывается
из процедуры Loto, но нет никаких препятствий, чтобы ее вызвали и в
любой другой задаче, где надо вычислять факториал;
· чтоб сделать функцию Factorial общедоступной
надо ее
скомпилировать и поместить в доступную библиотеку;
другой
вариант - в проект подключить текст этой программы
· чтоб собрать готовую
программу из
главной программы Loto_2_games, процедуры Loto и ПП-функции
Factorial
надо
либо сложить все тексты ПЕ стопочкой, не важно в каком порядке,
либо каждую ПЕ оформить в виде самостоятельного текста и включить в проект
Программные
единицы и вспомогательные операторы
Текст решения сложной задачи можно разделить на части.
В одной части текста можно разместить одну или несколько программных единиц
(ПЕ). ПЕ – это минимально допустимая часть текста решения сложной задачи, которую
можно оформить как самостоятельный текстовый файл, компилируемый, помещаемый в
библиотеку. Все остальные операторы неисполняемые и исполняемые размещают
внутри ПЕ. Используя компоновщик, из ПЕ можно собрать готовую к исполнению
программу. ПЕ - это главная программа, внешняя функция, внешняя процедура или
Модуль. Главная программа - единственная в задаче.
ПЕ
относят к особому виду составных операторов:
· Program имяРек.. .. end
Program имяРек - главная программа
с нее начинается решение задачи, а по end - нормально завершается задача
и текст главной программы
· Subroutine имяРек..
.. end Subroutine имяРек - это ПП-процедура
она решает какую-либо подзадачу,
по end - нормально завершается подзадача и текст процедуры
· Function имяРек..
.. end Function имяРек - это ПП-функция
она вычисляет какую-либо функцию, по end
нормально завершается вычисление функции и текст ПП-функции
· Module имяРек .. .. end Module имяРек
- это модуль
в модуле нет действий, поэтому по end
Module имяРек
оканчивается только текст модуля,
в модуле аккумулируются интерфейсы, а
также общедоступные данные и ПП для их обработки
Обе вместе Subroutine и Function
будем называть подпрограммами (ПП).
К числу вспомогательных
операторов и атрибутов относят:
End*
|
оператор
|
конец ПЕ
нормальное завершение
задачи нормальное завершение подзадачи
и/или конец текста ПЕ
|
Stop
|
оператор
|
досрочное завершение задачи в любой ПЕ
|
Return
|
оператор
|
досрочное завершение подзадачи (ПП),
но не всей задачи
|
Pause
|
оператор
|
пауза до нажатия Enter
|
Interface
.. ..
end Interface
|
Составной неисполняемый оператор
|
явное описание интерфейса или шаблона для вызова ПП на
Фортране или на другом языке
|
Include**
|
Оператор среди описаний
|
включить описания и определения в текст ПЕ
|
Use имяРек
|
Оператор после заголовка ПЕ
|
использовать модуль
|
Call имяРек(..A)
|
оператор
|
Вызвать процедуру с аргументами A
|
имяРек(..A)
|
Упоминание
в формуле
|
Вызвать функцию с аргументами A
в формуле вызывающей программы
|
intent(in)
intent(out)
intent(inout)
|
Атрибут или оператор в ПП
|
Входной intent -вид связи параметра ПП
выходной
входной- выходной
|
External***
|
Атрибут или оператор
|
внешние функции, используемые как аргументы при вызове ПП
|
intrinsic***
|
Атрибут или оператор
|
внутренние функции Ф90, используемые как аргументы ПП
|
Optional
|
Атрибут или оператор в ПП
|
атрибут необязательного параметра
|
Public/private
|
Атрибут или оператор в модуле
|
глобальные/локальные объекты модуля
|
common**
|
оператор
|
Общие блоки памяти
|
(*) Имя программы и вид программы можно повторить в
операторе end;
для модуля, модульных и внутренних программ – это делать
обязательно
(**) оператор, объявленный устаревшим в Ф90
(***) имена подпрограмм или функций, передаваемых как
аргументы
Классификация
ПЕ в Фортран-90
Будем формально придерживаться двух способов
классификации
- по
видам программ, с указанным ключевым словом в
заголовке
- по
месту относительно других ПЕ
Фортран-90: Классификация программ по
видам
К особому виду составных операторов относят
программные единицы (ПЕ), как-то:
Program Subroutine Function
Module.
Обе вместе Subroutine и
Function будем называть подпрограммами (ПП).
По
форме написания и назначению в Фортране различают следующие виды
программ:
- Встроенная
или стандартная функция, например, sin(x) – определена стандартом языка
Фортран-90
- Программная единица (ПЕ) –
начинается с заголовка Program Subroutine Function Module и
заканчивается end с указанием вида и имени ПЕ,
например Module M .. .. end Module M
- ПЕ
главная программа - заголовок program – в каждой задаче единственная
- ПЕ
- подпрограмма (ПП) – процедура или функция
- ПП
– с заголовком Subroutine будем называть процедурой.
- ПП
– с заголовком Function будем называть ПП-функцией.
- Операторная функция не
образует самостоятельной ПЕ и описывается
простым оператором описания вида F(x,y..)=выражение
- Интерфейс или шаблон
ПП - составной оператор описания, не образующий самостоятельную
ПЕ
Interface
.. .. end Interface
- ПЕ
модуль – заголовок module – только в Ф90
Способ
применения различается для разных
видов программ
- Главную программу Program загружают средствами
операционной системы
- Стандартную функцию упоминают
в выражении имяF(список
аргументов)
- Вызов
ПП-функции Function по
форме ничем не отличается от вызова стандартных функций
- процедуру Subroutine вызывают специальным оператором CALL имяS(список аргументов)
- модули используют в операторе USE имяM,
а не вызывают (в них нет действий),
это дает возможность присоединить и сделать видимыми
во внешней ПЕ глобальные (public) объекты модуля
- вызов
ПП может сопровождаться передачей предусмотренных параметров
- для
внутренней ПП к ее собственным описаниям присоединяются и становятся видимыми объекты программы-носителя с другими
именами
- для
модульной ПП к ее собственным описаниям присоединяются и становятся видимыми объекты модуля-носителя с другими именами
Начинающему
программисту не просто сделать обоснованный выбор, оформляя программу в
надлежащем виде. Примеры четырех вариантов оформления одной и той же задачи
вычисления факториала см. в “учебном проекте”, в примере 4.1, 4.2, 4.3, 4.4.
Чтобы легче понять разницу между всевозможными видами
программ, классифицируем их по двум направлениям:
· по горизонтали - сколько
исполняемых операторов в программе по принципу "0"
"1" "много"
· по вертикали - сколько результатов
(выходов), возвращаемых в вызывающую ПЕ, считая подпрограмму черным ящиком, у
которого при
вызове
· "много входов - много выходов"
· "много
входов - 1 выход"
· "ни
входов - ни выходов"
Используя
таблицу, проведем сравнительную классификацию программы по видам. В правую
колонку "много" попали три основных вида ПЕ. Только многими
операторами можно запрограммировать сложную задачу, разделенную на подзадачи в
виде ПП.
По
разным причинам в колонке “0” , то есть без исполняемых операторов,
сосредоточено 4 представителя
· Интерфейсы функций и процедур, которые в Ф90 не образуют
самостоятельный вид ПЕ, а числятся прямо среди описаний вызывающей ПЕ; либо
опосредованно - среди описаний в используемых модулях
- стандартные
функции, реализованные, скорее всего не на Фортране
- Модуль, являющийся
самостоятельным видом ПЕ, не содержащим выполняемых операторов и состоящим
- из интерфейсов вызываемых ПП
- из описаний локализованных в модуле private-объектов
- из описаний общедоступных public-объектов
- из модульных ПП для обработки public-объектов
Операторная
функция состоит из единственной строчки ИМЯ(..v) = e , которая содержит
формулу, по форме напоминающей оператор присваивания. Ее можно считать как
исполняемым оператором, так и определением. Числится она среди описаний, и
пользуется описаниями программы, в которой упоминается.
ПЕ вызывают по цепочке, начинающейся с вызова Prorgram
из DOS или WINDOWS. ПП-функцию Function и ПП Subroutine
вызывают по-разному:
- ПП-функцию
упоминают в формуле, куда она возвращает вычисленное значение
- ПП-процедуру
вызывают специальным оператором call ИМЯ(..v)
Часто по своему усмотрению программист может оформить
ПП и как Function и как Subroutine. Умение удачно разделить
задачу на подзадачи и выбрать подходящий вид ПЕ приходит с опытом. При большом
числе входов/выходов удобнее передавать данные не через параметры, а через
посредство заимствования объектов. Передача объектов через общие блоки памяти,
описываемые в операторе common, относят к
числу устаревающих конструкций.
В
центральной строке "1" сосредоточены все 4 вида функций, каждую из
которых независимо от способа определения, можно вызывать единообразно, просто
упоминая в формуле вызывающей программы. Способ определения зависит от числа
исполняемых операторов, написанных на Фортране. Для стандартной и
интерфейсной функции (ИФ) исполняемых операторов просто нет. Точнее говоря, они
написаны в другом месте:
- стандартная
функция, скорее всего, на Ассемблере, но не на Фортране
- интерфейсная
функция, возможно, например на “С”, а не на Фортране
- интерфейсная
функция, возможно, например, и на Фортране, но в другом месте, например,
кем-то, написана, скомпилирована и помещена в библиотеку, к примеру, IMSL
Операторная функция (ОФ) уникальна ввиду наличия
единственного исполняемого/описательного оператора. Говоря точнее, ОФ - это не
самостоятельная ПЕ, а конструктивное определение, действующее только в
той ПЕ, чьими обозначениями она пользуется. Только внутри этой ПЕ и можно
вызывать такую функцию, так как выражение e, зависящее от переменных ..v
записано в терминах этой ПЕ. В другой ПЕ оно не действует, так как
обозначения разных ПЕ независимы. В выражение e помимо параметров
могут входить и другие переменные вызывающей программы - они
подставляются по значению на момент вызова ОФ. ПП вида Function ИМЯ
(..P) относится
к основному виду и является функцией, оформленной в виде самостоятельной ПЕ,
содержащей любое число операторов. Внутри этой ПП ИМЯ используется как
переменная, которой присваивается значение функции, например, в операторе
присваивания ИМЯ=e , здесь e - выражение. ИМЯ –
одновременно и название функции, и переменная, отнесенная к одному из 5 базовых
типов данных или производному типу данных (только в Ф90). По размерности и типу
в Ф90 для функции возможны разные случаи
1. функция может быть скаляром, как раньше в Ф77 (тогда
это был единственный вариант)
2. функция может быть массивоподобной, то есть
быть вектором или многомерной таблицей
3. функция может быть элементной, будучи
определена и как скаляр, и как массивоподобная функция; в зависимости от
размерности аргументов станет или скаляром или вектором
4. функция может перенимать тип у аргументов
Так
как системы обозначений разных ПЕ независимы, то в вызывающей программе либо в
модуле
· в случае 1 обязательно надо описать хотя бы тип
функции
·
в случаях 2,3,4 обязательно надо описать
явный интерфейс, чтобы использовать механизм перегрузки
Интерфейсы
функций и интерфейсы процедур особенно важны при смешанном
программировании на “С” и Фортране.
Интерфейсы
функций и интерфейсы процедур поставляются в составе фирменных пакетов,
одинаково доступных и в “С” и в Фортране.
Тексты
интерфейсных функций и процедур низкоуровневой графики.
· подключают
оператором include ' *.fi ' в начале ПЕ для ДОС
· используя
модуль use MSFLIB в Windows
Богатая, ориентированная на Windows библиотека
“С”, может быть использована в Фортране, если написать собственные интерфейсы,
что требует понимания Фортрана и в какой-то мере “С”. Так можно применить и
функции из dll, реализующих саму среду Windows.
Применение функций из динамически связываемых библиотек dll
требует их предварительной регистрации. Процедура на “С” истолковывается как Function
либо Subroutine с соответственным вызовом. Вызов стандартных функций
так естественен, что кое-кто даже не догадывается, что за этим порой стоит
программа, для повышения эффективности написанная на машинно-ориентированном
Ассемблере.
В
нижней строке "0" показаны два черных ящика без передачи данных через
параметры:
- Program
- начинает и нормально завершает решение задачи (по ее end);
- Module не
содержит выполняемых операторов, но в нем упакованы данные вместе с ПП для
их обработки
Фортран-90: Классификация программ по местоположению
Внешние ПЕ могут следовать друг за другом в тексте или
составлять самостоятельный текст. Внутренние ПП вкладываются во внешние ПЕ
вслед за словом “contains”. Внутренние ПП также вкладываются аналогично в
модульные ПП. Модульные ПП аналогично вкладываются в модуль. Сам модуль не
содержит исполняемых операторов, но содержит описания и, возможно, модульные
ПП. В модульных ПП исполняемые операторы есть.
По местоположению относительно других ПЕ различают
- внешняя ПЕ – это программа
вида Program Subroutine
Function, которая записана в
виде текста, расположенного обязательно не внутри другой ПЕ. Внешние ПЕ
оформляются
либо в виде самостоятельного текстового
файла
либо следуют подряд по тексту наравне с
другими внешними ПЕ в общем текстовом файле
- модуль – это
самостоятельная ПЕ без исполняемых операторов, но, возможно, с модульными
ПП
- модульные ПП находятся
внутри модуля после CONTAINS
перед END
- внутренние ПП находятся
внутри какой-либо ПЕ-носителя после CONTAINS перед
END ПЕ-носителя
- носителем по отношению к
внутренним ПП могут быть ПЕ вида Program, внешняя поцедура
Subroutine, внешняя ПП-функция Function, модульная ПП
В заключение, используя обе классификации, дадим
сводную таблицу программ Ф90 с комментариями.
|
Вид программы
|
Структура программы
|
Примечания
|
1
|
Главная
|
Prorgram ИмяPr ! нет параметров
.. ..
CONTAINS
пачка Внутренних ПП
end
|
Единственная в задаче.
Имеет свои локальные объекты и использует публичные объекты
используемых модулей, предоставляя их внутренним ПЕ
самостоятельный вид ПЕ
|
2
|
Внешняя процедура
|
Subroutine ИмяS(параметры)
.. ..
CONTAINS
пачка Внутренних подпрограмм
end Subroutine ИмяS
|
Не внутри другой ПЕ.
Имеет свои локальные объекты и использует публичные
объекты используемых модулей, предоставляя их внутренним ПП
самостоятельный вид ПЕ
|
3
|
Внешняя
функция
|
Function ИмяF(параметры)
Тип ИмяF
.. .. в том числе ИмяF=
формула
CONTAINS
пачка Внутренних подпрограмм
end Function ИмяF
|
Не внутри другой ПЕ.
Имеет свои локальные обозначения и использует публичные
объекты используемых модулей, предоставляя их внутренним ПП
самостоятельный вид ПЕ
|
4
|
Встроенная
функция
|
Вызывается в выражении
t =Sin(X+0.5) +2.3
|
функция определена в языке
|
5,6
|
Модульная ПП -процедура или функция
|
После 1-го CONTAINS модуля
Subroutine или Function
|
Видит объекты модуля и имеет свои локальные объекты,
предоставляя их внутренним ПП
Внутри модуля
|
7,8
|
Внутренняя ПП -процедура или функция
|
После единственного CONTAINS
главной, внешней или модульной ПЕ Subroutine или Function
|
Видит объекты своей внешней ПЕ и имеет свои локальные
объекты
Внутри внешней или модульной ПП
|
9
|
Операторная функция
|
Dist(x1,y1, x2,y2) = sqrt((x1-x2)**2+(
y1-y2)**2)
Dr=dist( x,y, 5.,1.56)
|
Видима только внутри ПЕ, где определена. Упоминается в
выражениях среди действий.
среди описаний какой-либо ПЕ
|
10
|
Модуль
|
Module ИмяM
! нет параметров
Описания
contains
модульные ПЕ
.. ..
end Module ИмяM
|
Публичные объекты свои и используемых модулей
предоставляет внешним ПЕ. Имеются свои локальные объекты, предоставляемые
Модульным ПП
самостоятельный вид ПЕ
|
11
|
Явные интерфейсы или шаблоны для вызова ПЕ
|
Interface
Subroutine ИмяS(параметры)
только описания параметров
end Subroutine ИмяS
End Interface
|
указываются среди описаний в модуле или в вызывающей ПЕ
описания параметров – локальны в интерфейсе
среди описаний какой-либо ПЕ
|
Примечание: вид программы и имя программы в операторе end указывать
желательно,
а для модульных и внутренних программ - обязательно.
Фортран-77: Виды
программ
Возможности
Ф77 были скромнее, и в нем различали лишь следующие виды программ:
- главная
программа
- самостоятельная
ПП - это процедура или функция со своими обозначениями
- несамостоятельная
- операторная функция – единственная строка F(x,y,..)=формула
среди описаний программы
- от
MS в расширение Фортран-77 - интерфейсы ПП
В Ф77
нет деления на внешние и внутренние программы: ПЕ друг у друга никаких объектов
не заимствуют, и полностью независимы друг от друга по обозначениям объектов.
Они могут передавать данные через параметры или иметь общие блоки памяти,
упомянутые в описаниях. В заключение дадим сводную таблицу всех видов ПЕ Ф77
1
|
Главная программа
|
Prorgram ИмяPr
.. ..
end
|
ГП - единственная в задаче,
Имеет свои уникальные
объекты
Общие блоки памяти
|
2
|
ПП-Процедура
|
Subroutine ИмяS(параметры)
.. ..
end
|
Свои уникальные объекты
Общие блоки памяти
|
3
|
ПП-Функция
|
Function ИмяF(параметры)
Тип ИмяF
.. .. в том
числе ИмяF= формула
end
|
Свои уникальные объекты
Общие блоки памяти
|
4
|
Встроенная или стандартная
функция
|
Упоминается в выражении
t =Sin(X+0.5) +2.3
|
Стандартная функция
определена в языке
|
5
|
Операторная функция
|
F(x,y,..)= формула среди описаний
Dist(x1,y1, x2,y2) = sqrt((x1-x2)**2+( y1-y2)**2)
|
Видима только внутри ПЕ,
где определена. Упоминается в выражении среди действий
Dr=dist(
x,y, 5.,1.5)
|
Механизм вызова процедур и
функций, передача данных через параметры и путем заимствования
Механизм
вызова программ различается
·
главную программу загружают средствами операционной системы.
·
процедуру - вызывают,
используя оператор CALL Имя(..Аргументы) - после имени в
скобках через запятую перечисляются аргументы или как их еще называют фактические
параметры
· любую
функцию и стандартную, и нестандартную вызывают, упомянув ее имя в
формуле вызывающей программы
Имя(..Аргументы)
- после имени в скобках через запятую перечисляются аргументы
·
каждая из программ может вызывать все
виды программ, кроме чужих внутренних подпрограмм; вызываемая подпрограмма
в свою очередь может стать вызывающей для других подпрограмм,
разрешается даже вызов самое себя, то есть рекурсия
· модуль не вызывают, а
используют с помощью use Имя,
обязательно сразу после заголовка
программы; один модуль может использовать другой модуль, но модулю никак нельзя
использовать самого себя ни напрямую, ни через цепочку модулей.
Обмен
данными между программами осуществляется одним из указанных ниже способов
- сопоставляя аргументы вызывающей
программы с параметрами вызываемой программы
- делая видимыми
объекты из модуля M при
использовании его с помощью оператора Use M
- делая видимыми локальные
объекты внешней программы-носителя для всех внутренних подпрограмм,
помещенных после contains
Рассмотрим как данные кочуют из вызывающей
программы в вызываемую программу и обратно.
В
Ф77 все ПЕ были полностью независимыми (как мы сказали бы в Ф90-внешними ПЕ).
Каждая ПЕ имела свои собственные, независимые от других обозначения и описания
объектов. Данные можно было передавать только двумя способами:
1. - через параметры, сопоставляя аргументы формальным
параметрам
2. - через общие блоки памяти, как в “C”
В
Ф-90 обмен данными между программами стал более разнообразным, и данные теперь
можно передавать:
1. - через параметры, сопоставляя аргументы формальным
параметрам
2. - поименно заимствуя объекты для внутренних программ у
внешней программы-носителя
3. - поименно заимствуя объекты у модуля для внешних
программ и транзитом - для внутренних программ
4. - устаревший С-подобный способ - через общие блоки
памяти
Передача данных через параметры
Этот способ применяется во всех языках
программирования. Так делают и в стандартных программах. Так же делают и
в тех случаях, когда, варьируя небольшое количество параметров, можно легко
настраивать свои ПП на выполнения тех или иных действий.
Фактические параметры являются объектами вызывающей программы. Фактические
параметры перечисляются при вызове ПП после названия вызываемой
подпрограммы.
Формальные
параметры являются объектами вызываемой программы.
Формальные параметры указываются в заголовке ПП после ее названия.
Формальные параметры нужны для понимания текста ПП, под них даже не
отводится памяти. Например, если это массив, то в ПП передается адрес, где
лежит этот массив в вызывающей ПЕ.
Формальные параметры по виду связи, описываемому
с помощью атрибута Intent, подразделяются на три вида,
·
Intent (in) - входные параметры
·
Intent (out) - выходные параметры
·
Intent (inout) - входные-
выходные или изменяющиеся параметры
Фактические параметры (аргументы), указанные
при вызове ПП сопоставляются с формальными параметрами,
указанными в заголовке вызываемой программы.
Для
процедуры сопоставление производится так
- объекты вызывающей ПЕ в
виде аргументов, поступают на вход ПП и становятся значениями
сопоставленных им формальных параметров вида in или inout
для Subroutine
- объекты
вызываемой ПП, являющиеся результатами, поступают на выход ПП
- формальные параметры процедуры Subroutine вида out или inout становятся
значениями сопоставленных им фактических параметров
|
Вызывающая ПЕ call
ИмяS(..A),
где ..A-фактические параметры
- объекты вызывающей ПЕ
|
|
Туда
Aà P
|
Вызываемая ПП
SubroutineИмяS(..P)
где P -формальные параметры
- объекты вызываемой ПП
|
PàA Обратно
|
при вызове ПП
call ИмяS(..A) передаем
аргументы (..A)
на
вход ПП
|
аргументы становятся значениями формальных параметров
(..P)
входные
in AàP
входные-выходные inout AàP
|
Аргументы
обрабатываются
исполняемыми
операторами
и
формируются
результаты
|
Передаются через
посредство формальных параметров
Inout
выходных входных
pàA
out
выходных
pàA
|
При выходе из ПП
результаты
становятся
значениями
фактических параметров
|
сопоставляются
попарно
A/P
|
Сопоставляются попарно
P/A
|
Для
ПП-функции сопоставление производится так
- объекты вызывающей ПЕ в
виде аргументов, поступают на вход ПП и становятся значениями
сопоставленных им формальных параметров вида in для
Function
- единственный результат,
названный именем ИмяF с выхода ПП подставляется непосредственно
в формулу вызывающей ПП
|
Упоминается в формуле вызывающей ПЕ ИмяF(..A),
где ..A - фактические параметры
- объекты вызывающей ПЕ
|
|
Туда Aà P
|
Вызываемая ПП
Function ИмяF (..P)
где P -формальные параметры - объекты
вызываемой ПП
|
Только ИмяF обратно
|
При вызове ПП
ИмяF (..A) передаем
аргументы (..A)
на
вход ПП
|
аргументы становятся значениями формальных параметров
(..P)
есть только входные
in AàP
|
Исполняемые
операторы
обязательно
вычисляют единственный
результат
ИмяF
|
результат передается
через
имя функции
ИмяF
|
При выходе из ПП единственный
результат подставляется в формулу вызывающей программы
|
сопоставляются
попарно
A/P
|
передается по имени функции ИмяF
|
Сопоставление параметров
выполняется по определенным правилам:
- имена параметров в
вызывающей и вызываемой программе независимы друг от друга и не
обязательно совпадают
- параметры сопоставляются
- либо попарно в порядке следования
- либо явным указанием сопоставляемых пар при вызове в виде ключевых
параметров
имя_формального_параметра=фактический_параметр
- необязательные параметры, имеющие в вызываемой программе
атрибут optional, можно
опустить при вызове
- типы, вид связи и форма
сопоставляемых объектов вызывающей и вызываемой программ должны быть попарно
согласованными
- используя механизм перегрузки
с помощью задания кратного интерфейса, можно варьировать тип и количество
параметров, сделать функции элементными
- необязательные, ключевые и варьируемые по типу и форме параметры
нестандартных ПП, массивоподобные функции допустимы только при наличии
явного интерфейса
- необязательные, ключевые и варьируемые по типу и форме параметры
имеются и у стандартных ПП, их интерфейсы прописаны в стандартном модуле (use MSFLIB - для FPS 4.0)
Механизм
вызова Subroutine и Function различаются:
1) по форме вызова
процедуру вызывают специальным
оператором call ИМЯ(..v),
функцию вызывают, просто
упоминая имя в формуле вызывающей программы ИМЯ(..v)
2) по количеству результатов
для Function -
единственный результат, совпадающий с именем функции
для Subroutine - сколько
угодно результатов
3) по виду связи входных параметров
только intent(in) для Function
intent(in) и intent(inout) для Subroutine
4) по способу передачи результатов при возврате
из функции вычисленное значение
подставляется непосредственно в формулу вызывающей программы
из Subroutine все результаты передаются через формальные параметры вида out и inout в
сопоставленные им фактические параметры
5) Subroutine
может передавать результаты вне связи с параметрами, пользуясь
механизмом заимствования
- В
процессе решения задачи вызывающей может стать любая ПЕ,
выполняющаяся в данный момент, как это предусмотрено программой.
Механизм
вызова ПП Subroutine и Function схожи в том, что
- при входе в ПП
выделяется память под автоматические массивы
передаются данные из аргументов в сопоставленные им формальные параметры
- ПП могут получать
данные вне связи с параметрами, пользуясь механизмом заимствования
- по
end главной программы закрываются все файлы, освобождается память,
и решение задачи заканчивается
- по
stop в ПЕ любого вида решение задачи заканчивается, как и по end
главной программы, но на консоль дополнительно выдается сообщение
о прекращении работы "program terminated"
- по
stop ‘текст’ на консоль дополнительно выводится текст’, например,
stop ‘конец работы из-за ошибок’
- по
stop 1000 формируется код завершения
задачи, равный 1000 для передачи в операционную систему
- по
оператору pause задача приостанавливается до нажатия любой клавиши
- по
pause ‘текст’ дополнительно на консоль выводится
текст
- end в процедуре завершает ее работу,
освобождается память, выделенная под автоматические массивы,
передаются данные из формальных параметров в аргументы, сопоставленные им
процедура может возвращать результаты вне связи с параметрами,
пользуясь механизмом заимствования
- end в ПП-функции завершает ее работу,
освобождается память, выделенная под автоматические массивы,
через имя функции передается единственный результат и подставляется
в формулу вызывающей программы
- return досрочно
завершает выполнение ПП и выполняет действия как по end соответствующего
вида ПП
Рекурсия, то есть вызов самое себя прямо или по цепочке
- в
Ф77 не разрешается
- в
Ф90 разрешается
- ни
в каком виде не разрешается по использованию модулей (через посредство use)
Область видимости объектов, механизм заимствования
Область видимости характеризует те объекты, над которыми можно выполнять действия в той
или иной программе. В Ф77, где система обозначений каждой ПЕ была полностью
независима от других ПЕ, область видимости - это просто все объекты этой ПЕ.
Область видимости объектов такой ПЕ нарисуем, направив стрелку со стороны
действий на объявленные объекты
В
Ф-90 обмен данными между программами стал более разнообразным, так как данные
можно теперь передавать:
·
через параметры, сопоставляя аргументы
формальным параметрам
·
поименно заимствуя объекты у внешней
программы-носителя для внутренних программ, похоже на Паскаль
·
поименно заимствуя объекты у модуля для
внешних программ, нет аналога ни в “C” ни в Паскале
·
поименно заимствуя объекты у модуля
транзитом через внешние программы - для внутренних программ
·
устаревший С-подобный способ - через
общие блоки памяти по адресам внутри блока
По
этой причине область видимости расширилась и усложнилась, стали различать
- локальные объекты, которые видимы только в ПЕ, где они
описаны
- глобальные объекты, которые видимы не только в ПЕ, где они
описаны, но и во внутренних ПП
- глобальные private-объекты, которые видимы только внутри модуля, где они
описаны
- глобальные public-объекты, которые видимы там, где модуль использован
Область видимости локальных объектов и глобальных объектов для программы-носителя
В Ф90 программой-носителем
может быть
- главная программа
- внешняя процедура
- внешняя функция
- модуль
- модульная процедура
- модульная функция
Программа-носитель
предоставляет свои объекты и делает их видимыми для всех подпрограмм вложенных
в нее. Вложение производится в конец текста программы между операторами contains и end.
Если вложений нет, то contains
опускают.
Программа имеет структуру и
порядок следования операторов, представленные в таблице
Заголовок ПЕ на
выбор:
·
PROGRAM
Имя
·
SUBROUTINE Имя(..P)
·
FUNCTION
Имя(..P)
·
MODULE Имя
|
Определяет вид программы-носителя
главная программа –
обязательно без параметров
Внешняя процедура либо модульная
процедура *)
внешняя функция либо модульная
функция *)
модуль– обязательно без
параметров
..P список
формальных параметров
|
! комментарии
|
Располагаются в любом месте программы
|
Use ИмяМ1
|
Æ видим глобальные public-объекты
в модуле ИмяМ1
Æ доступны процедуры и функции из модуля
ИмяМ1
+ применимы шаблоны из модуля ИмяМ1
для вызова ПП
|
Implicit none
|
ç тотальный контроль за наличием описания для каждого
объекта
|
Interface
.. ..
end Interface
|
Ë-
шаблоны для вызова внешних ПП , включая заголовок с параметрами
и их описания, взятые прямо из текста самой ПП
|
Type .. .. endType
|
Объявления производных типов
|
parameter Real integer complex logical character
Type
dimension
|
Описания констант, переменных и массивов
- для формальных параметров
..P
- для глобальных объектов программы-носителя Имя
списки
ввода-вывода namelist
|
m
format(..f)
|
- формат вывода – описание, которое, в отличие от
других, -
в любом месте, даже среди исполняемых операторов
|
Исполняемые операторы
|
В порядке выполнения, но можно его изменить с
помощью do if where selectCase return
stop
|
Модуль – без Исполняемых
операторов
|
CONTAINS
|
Отделяет носитель от вложенных ПП
- не нужен, если вложенных ПП нет
- далее ПП, для которых Имя - носитель данных
|
Subroutine ИмяS1
.. ..
end Subroutine ИмяS1
Function ИмяF1
.. ..
end Function ИмяF1
|
Здесь располагаются ПП
·
Внутренние, заимствующие объекты у программы-носителя
·
модульные, заимствующие объекты у модуля
|
END ·
PROGRAM Имя
·
SUBROUTINE Имя
·
FUNCTION Имя
· MODULE
Имя
|
Конец текста программы
с именем - можно просто end
Конец текста внешней процедуры
лучше с именем
Конец текста модульной
процедуры обязательно с именем
Конец текста внешней функции
лучше с именем
Конец текста модульной
функции обязательно с именем
Конец текста модуля
обязательно с именем
|
*) находятся внутри модуля после contains
Связь
одноименных объектов программы-носителя и вложенной подпрограммы разрывается: в
каждой из программ свои объекты, не смотря на одинаковые имена !
Область видимости локальных объектов и глобальных объектов для программной
единицы (ПЕ), использующей модуль
На
картинке показано присоединение глобальных объектов модуля в область видимости
внешней ПЕ
- оператор
use ИмяМодуля заимствует у модуля и делает видимыми глобальные
объекты модуля, что показано в виде бирюзового конуса
- толстой
пунктирной синей линией показано, на какие глобальные объекты модуля
непосредственно направлено действие команд внешней ПЕ
- толстой
сплошной синей линией обозначен вызов модульных подпрограмм, напрямую
объекты модуля не упоминаются
- косвенное
обращение к глобальным объектам модуля со стороны внешней ПЕ показано
транзитом через вызов модульных ПП (толстая сплошная синяя линия) и далее
посредством команд модульной ПП (сплошная жирная желтая линия)
Программирование только с использованием глобальных
объектов через посредство модульных ПП называют инкапсуляцией. Такая манера работы позволяет
разделить программу и данные, что важно при длительном сопровождении программы.
В этом случае изменение структуры данных внутри модуля, возможно, затронет
модульные ПП, но не повлечет за собой изменение внешних программ.
Возможны различные сочетания вариантов присоединения
глобальных объектов
- сочетание
обеих возможностей - заимствование объектов и у носителя, и у модуля
- Модуль
использует другой модуль, присоединяя его объекты
- Внутренняя
подпрограмма использует объекты модуля не напрямую, а через посредство
носителя
- При
использовании модуля можно заказать только (only)
какие-то из его объектов, например, для устранения коллизии имен
- При
использовании модуля можно заменить при помощи составного знака =>
некоторые из используемых имен на свои названия, например, для устранения
коллизии имен
- По
вызовам ПП зацикливание возможно (это вызов, в конечном счете,
самое себя - рекурсия)
- По
использованию модулей рекурсия запрещается. Использование
самое себя то есть определение само через себя абсурдно.
В
Ф90 появилось принципиально новое понятие Модуля. Модули предназначены для
интегрирования большого количества данных, циркулирующих между многими
программами солидного проекта. Модули призваны заменить common и include, объявленные в Ф90 устаревшими.
Модуль
надо лишь упомянуть, чтоб стали видимыми его объекты, а каждый common-блок
надо прописывать со всеми его
объектами в каждой ПП, где эти объекты упоминаются, подгоняя адреса.
Оператор
include просто включает текст операторов описания в
текст программы. В отличие от него использование модуля
· только
в простейшем варианте присоединяет описание объектов, тем не менее, не удлиняя
текста программы
· а
также присоединяет шаблоны программ
· дает
механизм реализации принципа инкапсуляции
Модуль имеет структуру и порядок следования операторов,
представленные в таблице
Заголовок
MODULE Имя
|
модуль– обязательно без
параметров
|
! комментарии
|
Располагаются в любом месте программы
|
Use ИмяМ1
|
Модуль может использовать другие модули
Æ видим глобальные public-объекты
в модуле ИмяМ1
Æ доступны процедуры и функции из модуля
ИмяМ1
+ применимы шаблоны из модуля ИмяМ1
для вызова ПП
|
Implicit none
|
ç тотальный контроль за наличием описания для каждого
объекта
|
Interface
.. ..
end Interface
|
Ë-
шаблоны для вызова внешних ПП , включая заголовок с параметрами
и их описания, взятые прямо из текста самой ПП
|
Type .. .. endType
|
Объявления производных типов
|
parameter Real integer complex logical character
Type
dimension
public private
|
Описания констант, переменных и массивов
- для глобальных объектов модуля Имя
- и public-объекты,
и private-объекты видны
в модульных ПП
|
нет исполняемых
операторов
|
Модуль – без Исполняемых
операторов
|
CONTAINS
|
Отделяет носитель от вложенных модульных подпрограмм
- не нужен, если вложенных ПП нет
- далее ПП, для которых Имя - носитель данных
|
Subroutine ИмяS1
.. ..
end Subroutine ИмяS1
Function ИмяF1
.. ..
end Function ИмяF1
|
Здесь располагаются ПП
·
Внутренние, заимствующие объекты у программы-носителя
·
модульные, заимствующие объекты у модуля
Модульные ПП можно вызывать из внешних ПЕ, использующих
модуль Имя
|
END MODULE Имя
|
Конец текста модуля
обязательно с именем
|
Модуль предоставляет возможность объявить данные,
общедоступные для многих внешних ПЕ. В отличие от ПЕ непосредственно в модуле
нет действий. Однако внутри модуля после слова CONTAINS могут содержаться Модульные
ПП. В любую из модульных подпрограмм после слова CONTAINS можно вложить пачку
внутренних ПП. Модульные подпрограммы и их внутренние подпрограммы
заимствуют объекты (и Public, и Private) у модуля. Получается, что модуль - это трехэтажная
конструкция: модуль - модульные ПП - внутренние ПП. Важнейшее
предназначение модуля состоит в том, чтобы упаковать какие-либо данные, сделав
их доступными в главной программе или любой внешней ПП простым использованием
имени модуля в операторе USE ИмяМодуля. Приветствуется, чтобы
в модуле как бы в одной упаковке с данными были бы припрятаны и модульные
подпрограммы для обработки этих данных. Слово “припрятаны” носит принципиальный характер: чем
лучше продуман модуль - тем меньше есть причин, чтобы обращаться к данным,
минуя модульные ПП. В идеале модуль достигает совершенства, когда такого
доступа вообще нет.
Рассмотрим модули сначала
более простые и понятные по назначению, потом посложнее
MODULE
ИмяМ1
Public -
Общедоступные данные
End
MODULE ИмяМ1
|
ß×
|
Программа Имя1
Use ИмяМ1 …………
|
|
|
ß×
|
Программа Имя2
Use ИмяМ1 ………
|
MODULE ИмяМ2
Interface
Шаблоны всех внешних
процедур для централизованного контроля
End Interface
End
MODULE ИмяМ2
|
ß×
|
Программа Имя1
Use ИмяМ2, except_this_one => Имя1
все шаблоны ПП, кроме
данной Имя1
|
|
|
ß×
|
Программа Имя2
Use ИмяМ2, except_this_one=>Имя2
все шаблоны ПП, кроме
данной Имя2
|
MODULE ИмяMПрТип
Определение
производного типа данных
Contains
Все процедуры, функции доступа к данным этого типа
Например, как создать,
как выбрать, как занести,
как "обнулить"
End MODULE ИмяMПрТип
|
ß×
|
Программа Имя1
Use ИмяMПрТип
|
|
|
ß×
|
Программа Имя2
Use ИмяMПрТип
………
|
Сама система программирования
в Ф90 также опирается на использование модулей. Отметим самый важный модуль
use
MSFLIB – он содержит константы, объявления производных типов,
интерфейсы, функции
- для низкоуровневой графики
- для QuickWin – быстрого безболезненного перехода от ДОС к
стилю Windows
Общие
блоки памяти
Глобальные
объекты размещают в общих блоках памяти, которые по идеологии
наиболее близки к языку С. В Ф-90 это средство отнесли к устаревшим наряду
с новым устаревшим оператором include также весьма характерным для С.
После появления модулей эти средства кажутся действительно архаичными. В
модулях не надо так скрупулезно как в общих блоках памяти подсчитывать адреса и
отслеживать раскладку данных по разным блокам. Краткое описание приводится
только для понимания ранее написанных программ и чтобы убедиться в том, что
новые программы стоит писать только с использованием модулей.
Общие
блоки памяти – это старинная
альтернатива передаче данных через параметры, когда данных слишком много.
Оператор Common { /ИМЯ/ ..v } размещает в общем блоке /ИМЯ/
объекты в порядке перечисления их в списке ..v , здесь
ИМЯ - это глобальное имя, уникальное в рамках задачи. В одном
операторе Common можно описать несколько общих блоков памяти. В каждой
ПЕ описывают те общие блоки, объекты из которых упоминаются в программе.
Если
данные размещены в модуле, то достаточно упомянуть только модуль, не перечисляя
его объекты.
Допускаются
любое количество именованных блоков и один безымянный (у него /ИМЯ/
отсутствует). Если имена блоков являются глобальными, то имена объектов из
списка ..v локализованы в ПЕ. Сопоставление данных в разных
ПЕ выполняется только по адресам от начала блока, что в “C”
рассматривается как поля внутри записи. Как это делается поясняется на примере:
|
Subroutine S1 ! процедура 1
real a,r,X
common /Block/ a,r, X(10)
|
Subroutine S2 ! процедура 2
real F,G
common /Block/ f(2), G(20)
|
адрес в /Block/
1
5
9
11
..
|
/Block/ в S1
a 4 байта
r
4 байта
X1
4 байта
X2
4 байта
..
X10
4 байта
всего
48 байтов
|
/Block/ в S2
f1
4 байта
f2 4
байта
G1 4
байта
G2 4
байта
..
G20 4
байта
всего
88 байтов
|
Длину
блока в разных ПП можно варьировать: компоновщик отведет память по
максимуму.
Оператору
Common /ИМЯ/ ..v из Фортрана в языке "С"
соответствует
external struct { ..v } ИМЯ ; причем ИМЯ
должно состоять только из прописных букв.
Примеры функций, подпрограмм и программ
Примеры
1.1-1.3 иллюстрируют решение одной и той же задачи ”найти максимальный элемент
массива” несколькими способами
Пример 1.1. Встроенная функция max и процедура maximum.
Program maximum
! программа maximum
real X(12) , Xm
read (*,*) X ; write (*,*) ' массив X=' ,
X
call maximum (Xm,X )
write (*,*) ' Xmax=' , Xm
end
Program maximum
SUBROUTINE
maximum (Rmax,R )
real R(12) , Rmax
Rmax = R(1)
do k=2,12
Rmax = max ( R(k), Rmax )
! Встроенная
функция max (a,b)
end
do ! k=2,12
end
SUBROUTINE maximum
Пример 1.2. Встроенная функция max и функция maxima.
Program
maxima ! программа maxima
real R(12) , Rmax
read (*,*) R ; write (*,*) ' массив R=' ,
R
write (*,*) ' Rmax=' , Rmax(R)
end
Program maxima
real
FUNCTION Rmax (R )
real R(12)
Rmax = R(1)
do k=2,12
Rmax = max ( R(k), Rmax )
! Встроенная
функция max (a,b)
end
do ! k=2,12
end
FUNCTION Rmax
Пример 1.3. Встроенная функция maxVal - найти
максимальный элемент массива.
Program
maxima ! программа maxima
real R(12) , Rmax
read (*,*) R ; write (*,*) ' массив R=' ,
R
write (*,*) ' Rmax=' , maxVal(R)
end
Program maxima
Пример 2. Создание альтернативного интерфейса для работы с
целыми и вещественными массивами
INTERFACE
maxi ! generic name of maxi - родовое имя maxi для работы с целыми и вещественными массивами
real FUNCTION rmaxi (R )
real,intent(IN), dimension (1:12):: R
end FUNCTION rmaxi
integer FUNCTION imaxi (R )
integer,intent(IN) :: R(12)
end FUNCTION imaxi
END
INTERFACE maxi
Надо написать две ПП – одну rmaxi для поиска максимума в вещественном массиве,
другую imaxi
для поиска максимума в целом
массиве. Тогда показанный интерфейс позволит использовать родовое имя maxi одинаково
успешно и для целого и для вещественного массива. Иначе говоря, функция будет перенимать
тип у аргумента, используя так называемый механизм перегрузки.
Пример 3. Операторная функция.
Формулу
можно упростить
до t = ( s(a,2.5) - s(b,1) ) / s(a,b) , введя обозначение s(x,y) = .
Program
operFun ! То же самое напишем в
программе:
real
x,y, a,b, s, T
s(x,y) = sqrt(x*x - y*y ) ! определение операторной функции
read
(*,*) x,y ; write(*,*) ' x=' ,
x, ' y=' , y
T = ( s(a,2.5) - s(b,1.0) ) / s(a,b) ! многократный вызов
операторной функции
write(*,*)
' T = ', T
end
Program operFun
Пример 4. Иллюстрируется реализации одной и той же задачи
вычисления факториала, как программ разного вида.
Вычислить факториал n!
= 1*2*3*.. *n . Из математики известен особый случай: 0!=1
Пример 4.1. Повторяет подпрограмму-функцию Factorial из учебного
проекта.
Function Factorial ( n ) ! заголовок
подпрограммы-функции
real*8 Factorial ! тип результата
integer,intent(in) :: n ! единственный
входной параметр
integer k ! при умножении автоматически
переводится а двойную точность
Factorial
= 1
if ( n = = 0 ) return ! из математики известен
особый случай: 0!=1
do k=1,n
Factorial = Factorial *k
enddo
end Function Factorial
Пример 4.2. Функцию Factorial из учебного
проекта, можно определить
по-другому, используя стандартную векторную функцию product
Function Factorial ( n ) ! заголовок
подпрограммы-функции
Integer
k
real*8 Factorial
! тип
результата
if ( n > 0 ) then
Factorial =product( (/
( dble(k), k=1,n) /)
)
! dble(k) преобразует тип аргумента
в вещественный двойной точности
! ( dble(k), k=1,n ) - неявный цикл
!
(/ ( dble(k), k=1,n) /) -
конструктор массива
! product( (/ ( dble(k), k=1,n) /) ) -
векторная функция, вычисляющая произведение
Else
Factorial = 1
Endif
End Function Factorial
Пример 4.3. Функцию Factorial из учебного
проекта
можно оформить не только как
самостоятельную ПЕ, но и как операторную функцию, объявленную в составе ПП
Loto. Изменилось определение функции Factorial , но не изменился вызов функции Factorial.
Subroutine
Loto ( m,n, S,P ) ! заголовок подпрограммы Loto
Integer,intent(in) :: m, n ! (in) - входные параметры
real,intent(out) :: S,P ! (out) - выходные параметры
Integer k
! это описание используется в определении операторной функции
Factorial
real*8
Factorial ! тип (dble(k)) => тип(product) => тип(Factorial)
! операторная
функция в составе ПП Loto
Factorial(n) = product( (/ ( dble(k), k=1,max(1,n) ) /)
)
!
при n=0 получим max(1,0)=1,
! но при n=1 и при n>1 получим max(1,n)=n
S =
Factorial(n) / ( Factorial(m)* Factorial(n-m) ) ! вызов функции не меняется
P = 1 / S
End
Subroutine Loto
Пример 4.4. Реализует вычисление факториала из учебного
проекта вообще не в виде функции,
а в виде процедуры
- не изменился способ
вычисления факториала и имя входного
параметра n
- изменился вид ПП с function на Subroutine
и ее имя с Factorial на proFact, изменился интерфейс
- подпрограмму вида Subroutine по
имени proFact только вызывают, но с ее именем не связывают
результатов
- тот же результат Factorial стал выходным параметром вместо имени
функции
- в соответствии с
интерфейсом изменится вызов подпрограммы
interface
SUBROUTINE proFact ( n, Factorial) ! заголовок процедуры
integer,intent(in) :: n
!
как и был, единственный входной параметр
real*8 ,intent(out) :: Factorial
!
добавился выходной параметр вместо имени ПП-функции
end
SUBROUTINE
proFact
end interface
Оболочка, показанная
крупным жирным шрифтом, у ПП сменилась,
а начинка – нет.
SUBROUTINE proFact ( n, Factorial) ! заголовок процедуры
integer,intent(in) :: n ! единственный входной параметр
real*8 ,intent(out) :: Factorial ! тип результата
integer k
Factorial = 1
if ( n = = 0 ) return
do k=1,n
Factorial = Factorial *k
enddo
end SUBROUTINE proFact
В соответствии с интерфейсом
изменится вызов подпрограммы из ПП Loto - добавятся
новые промежуточные переменные factorN, factorN, factorNM
integer n,m; real*8 factorN,
factorN, factorNM, S
Call proFact(n,factorN)
Call proFact(m,factorM)
Call proFact(n-m,factorNM)
S = factorN / ( factorM)*factorNM )
Оценивая 4 варианта реализации вычисления факториала, можно
отметить следующее
- вычисление факториала предпочтительнее оформить не в виде
процедуры, см. 4.4, а в виде функции, которую легче вызывать
- это можно сделать, потому что результат – одно число
- вызов функции не меняется, если ее по-разному определять,
см. 4.1, 4.2, 4.3
- самое краткое определение – у
операторной функции, см. 4.3, видимо, из-за использования стандартных
функций product, dble,max
Пример 5.
Иллюстрация к использованию оператора external для
передачи имени программы
- пусть подпрограмма koren умеет
численно решать уравнение f(x)=0, находя один корень xkor
с заданной степенью точности eps
для любой функции f(x) в заданном интервале [a,b]
- требуется решить два уравнения
x3+x2-3x+4=0
(F1)
x3-x2-3x +
4=0 (F2)
- чтоб сделать подпрограмму koren действительно универсальной
надо передавать в нее имя подпрограммы-функции f(x), реализующей
левую часть уравнения f(x)=0
- объявим как External F1,F2 - внешние имена
функций для левых частей двух уравнений F1(x)=0 и
F2(x)=0
- имена ПП-функций F1 и F2, соответственно, передадим в качестве
фактических параметров в двух вызовах ПП koren
- сами по себе подпрограммы F1 и F2 крайне простые, но,
чтобы воспользоваться универсальной ПП koren, без
них не обойтись
- на рисунке показан график F2(x)=0 , ищется
корень x2~ =1.5 справа от оси OY в интервале [0,2]
- для понимания примера
приводится лишь интерфейс ПП koren
program External_for_koren
real :: F1,F2, eps=0.0001
real ::
a1 = -4, b1= -2, x1 !
корень x1 для F1(x)=0 в [-4,-2]
real :: a2=0, b2=2, x2 !
корень x2 для F2(x)=0 в [0,2]
External F1,F2
! имена внешних ПП-функций, вычисляющих левые части двух уравнений f(x)=0
interface
subroutine koren (xkor,a,b, eps, f ) ! решает любое уравнение f(x)=0
real, intent(in) :: a,b ! интервал [a,b] с
одним корнем
real, intent(in) :: f
! левая часть уравнения
f(x)=0
real, intent(in) :: eps ! точность для искомого корня
real, intent(out)
:: xkor ! это ответ - найденный корень
end
subroutine koren
end
interface
Call koren (x1,a1,b1, eps,
F1) ! для уравнения F1(x)=0 ! x1 ~ = -3 находится в [-4,-2]
Call koren
(x2,a2,b2, eps,
F2) ! для уравнения F2(x)=0 ! x2~
=1.5 находится в [0,2]
end program External_for_koren
real function F1(x)
real x
F1=x**3+x**2-3*x+4
End function F1
real function F2(x)
real x
F2=x**3-x**2-3**x+4
End function F2