современный фортран , Бортеньев. О. В. Бартеньев Современный Фортран
Скачать 2.24 Mb.
|
85 О. В. Бартеньев. Современный ФОРТРАН call pval( pt ) print *, pt ! 1.000000 -2.000000 end program gopo subroutine pval(pt) type point sequence real x, y end type type(point) pt pt.x = 1.0 pt.y = -2.0 end subroutine Два определения типа в разных программных единицах определяют один и тот же тип, если оба имеют одно и то же имя, обладают атрибутом SEQUENCE, их компоненты не являются приватными и согласуются в отношении порядка их следования, имен и атрибутов. Однако более рациональным представляется однократное описание производного типа в модуле с последующей ссылкой на модуль в программных единицах, использующих этот тип. Атрибут SEQUENCE также должен быть использован при размещении записи в common-блоке, например: program gopo type point sequence real x, y end type type(point) pt real s, t common /a/ s, pt, t call pval( ) print '(4f5.1)', s, pt, t ! 2.0 1.0 -2.0 -1.0 end program gopo subroutine pval( ) common /a/ s, x, y, t s = 2.0; t = -1.0 x = 1.0 ! x и y определяют компоненты y = -2.0 ! записи pt главной программы end subroutine 3.9.5. Запись как результат функции Результат функции может иметь производный тип, например внешняя функция mu (разд. 3.9.3) возвращает значение типа pair. При задании внешней функции производного типа следует описать этот тип как внутри функции, так и в каждой вызывающей функцию программной единице. Лучше всего для этих целей определить тип в 86 риложение3. Организация данных модуле и затем использовать use-ассоциирование. При явном определении типа и в функции, и в вызывающих ее программных единицах потребуется использование атрибута SEQUENCE. Сама же функция должна быть объявлена в каждой вызывающей ее программной единице. Если же имеющая производный тип функция является внутренней, то этот тип может быть описан только в программной единице, из которой эта функция вызывается. При этом тип функции определяется только в самой функции, например: module deta type pair real x, y end type pair end module deta program paw2 use deta ! Получаем доступ к типу pair type(pair) :: pt1 = pair(2.0, 2.0) type(pair) :: pt2 pt2 = mu(2.5, pt1) ! Вызов внутренней функции mu print *, pt2 ! 5.000000 5.000000 contains function mu(a, b) ! Внутренняя функция типа pair type(pair) mu ! Объявление типа функции type(pair), intent(in) :: b real, intent(in) :: a mu.x = a * b.x mu.y = a * b.y end function mu end program paw2 3.9.6. Пример работы с данными производного типа Рассмотрим пример, иллюстрирующий механизм передачи записей из программы в файл и обратно из файла в программу. Пусть файл c:\exam.dat содержит данные о результатах экзаменационной сессии студенческой группы. (Для генерации файла c:\exam.dat в программе использован датчик случайных чисел.) Каждой записью файла является строка табл. 3.3. Вывести на экран из созданного файла все его записи и среднюю оценку студентов. module tex type exam ! Структура exam character(30) name ! Студент integer(4) m1, m2, m3, m4 ! Экзаменационные оценки end type type(exam) stud ! stud - переменная типа exam integer :: unit = 2 ! Номер устройства подсоединения 87 О. В. Бартеньев. Современный ФОРТРАН end module tex ! файла c:\exam.dat program aval use tex ! Включаем описание структуры integer(4) :: ns = 20 ! Число студентов в группе real(4) :: am = 0.0 ! Средняя оценка студентов ! Открываем двоичный файл open(unit, file = 'c:\exam.dat', form = 'binary') call testfile(ns) ! Наполняем файл exam.dat rewind unit ! Переход на начало файла do while(.not. eof(unit)) ! Обработка данных файла read(unit) stud am = am + stud%m1 + stud%m2 + stud%m3 + stud%m4 write(*, '(1x, a20, 4i4)') stud ! Контрольный вывод end do close(unit) am = am / float(ns * 4) write(*, *) ' Средняя оценка группы: ', am end subroutine testfile(ns) use tex ! Включаем описание структуры integer ns, i integer sv write(*, '(1x, a $)') 'Старт random (INTEGER*4): ' read(*, *) sv call seed(sv) do i = 1, ns ! Имя студента имеет вид: Name номер, например, Name 01 write(stud%name, '(a, i3.2)') 'Name ', i stud%m1 = rmark( ) ! Генерируем экзаменационные оценки stud%m2 = rmark( ) ! Оценка - случайное число от 2 до 5 stud%m3 = rmark( ) stud%m4 = rmark( ) ! Последние 4 оператора можно заменить одним: ! stud = exam(stud%name, rmark( ), rmark( ), rmark( ), rmark( )) write(unit) stud ! Добавляем запись в файл end do contains integer function rmark( ) ! Генератор экзаменационных оценок real(4) rnd call random(rnd) ! rnd - случайное число типа REAL(4) (0.0 ≤ rnd < 1.0) rmark = nint(8.5 * rnd) ! Округление rmark = max(rmark, 2) ! Оценка не может быть менее двух rmark = min(rmark, 5) ! Оценка не может быть более пяти end function end Пояснения: 88 риложение3. Организация данных 1. В Фортране структурная переменная может быть записана "целиком" как в неформатный (двоичный), так и в текстовой файл (в более ранних версиях Фортрана в текстовом файле запись можно было сохранить лишь покомпонентно). При передаче записи stud в текстовой файл можно использовать, например, такой форматный вывод (пусть файл подсоединен к устройству 3): write(3, '(1x, a30, 4i3)') stud В задаче для хранения данных использован последовательный двоичный файл exam.dat. Передача в файл осуществляется в подпрограмме testfile. Каждая запись файла имеет вид: Name номер Оценка 1 Оценка 2 Оценка 3 Оценка 4 Начальное значение номера - 01. Строка является внутренним файлом; поэтому проще всего получить строку вида Name номер, записав в символьную переменную stud%name данные 'Name ', номер по формату '(a, i3.2)', где номер меняется от 1 до ns. Каждая оценка формируется случайным образом в диапазоне от 2 до 5 функцией rmark, которая, в свою очередь, использует генератор случайных чисел (от 0.0 до 1.0) - встроенную подпрограмму RANDOM. Формируемая последовательность оценок зависит от начальной установки RANDOM, которая определяется значением параметра подпрограммы SEED. 2. Символ $ в спецификации формата оператора WRITE(*, '(1x, a $)') обеспечивает вывод без продвижения, что позволяет ввести значение sv на той же строке, где выведено сообщение 'Старт random (INTEGER*4): '. 3. Выход из цикла происходит при достижении конца файла (функция EOF вырабатывает .TRUE.). В результате работы программы на экран будут выведены строки (последовательность оценок зависит от значения параметра sv): Name 01 2 4 5 5 Name 20 3 5 5 4 4. Чтобы избежать повторного описания структуры в главной программе и подпрограмме testfile, ее описание выполнено в отдельном модуле tex, который затем включается в текст программных единиц оператором USE. 3.9.7. Структуры и записи 3.9.7.1. Объявление и присваивание значений Фортран CVF и FPS наследует от предшествующих версий еще одну возможность объявления производного типа данных - оператор STRUC- TURE, которым, правда, не следует пользоваться при написании нового кода. Синтаксис оператора: 89 О. В. Бартеньев. Современный ФОРТРАН STRUCTURE /имя структуры/ объявление компонентов структуры END STRUCTURE Имя структуры - имя нового типа данных, оно не должно совпадать с именем другой переменной или функции, определенных в том же программном компоненте; также оно не может совпадать с именем встроенного типа данных, например COMPLEX. Объявление компонентов структуры - любая комбинация одного или нескольких операторов объявления типов переменных или конструкций UNION. Операторы объявления типов могут содержать простые переменные, массивы, строки и операторы RECORD, которые ссылаются на ранее определенные структуры. Элементы структуры объявляются без атрибутов. Имя структуры является локальным, и поэтому структура как тип должна быть объявлена (явно или в результате use-ассоциирования) в каждой программной единице, в которой необходимо работать с переменными введенного типа. Структуры, содержащие оператор RECORD, называются вложенными. Вложенные структуры могут содержать компоненты с одинаковыми именами. Длина структуры не может превышать 64 Кбайт. Способ упаковки структуры в памяти контролируется директивой $PACK и параметром /Zp в командной строке компилятора. Структуры являются одинаковыми, если их компоненты имеют одинаковый тип и размещены в структуре в одной и той же последовательности. Кроме этого, они должны иметь одинаковую упаковку в памяти ЭВМ. Переменные структурного типа объявляются оператором RECORD /имя структуры/ [, attrs] [::] vname attrs и vname имеют тот же смысл, что и в операторе объявления производного типа TYPE. Имя структуры должно быть введено до применения оператора RE- CORD. Оператор RECORD должен предшествовать исполняемым операторам программного компонента. Пример: structure /item_d/ ! Описание заказанной вещи character*20 descr, color, size ! Название, цвет, размер integer*2 qty ! Количество real*4 price ! Цена end structure structure /order/ ! Описание заказа integer*4 ordnum, cus_id ! Номер заказа, код покупателя record /item_d/ item(10) ! Переменная типа item_d 90 риложение3. Организация данных end structure record /order/ cur_ord ! Переменная типа order cur_ord = order(1200, 300, item_d('shorts', 'white', 'S', 1, 35.25)) print *, cur_ord.item(1) ! Вывод данных о первом предмете print *, cur_ord.item(2).color ! Вывод цвета второй вещи Запись состоит из компонентов, определенных оператором STRUC- TURE. Доступ к компоненту записи осуществляется посредством указания после имени структурной переменной (записи) точки или знака % и имени компонента, например: c_id = cur_ord.cus_id ! Код покупателя i_color = cur_ord.item.color ! Цвет изделия или c_id = cur_ord%cus_id i_color = cur_ord%item%color Компоненты записи не имеют отличий от других переменных, имеющих тот же тип, кроме одного: целочисленный элемент записи не может быть использован в качестве переменной (dovar) DO-цикла. При работе с текстовыми файлами можно выполнять форматный или управляемый списком В/В как компонентов записи, так и всей записи. 3.9.7.2. Создание объединений В ряде задач необходимо записывать в файл (или считывать из файла) последовательно одинаковые по размеру, но разные по составу записи. В Фортране имеется возможность выполнять эти действия, работая с одной и той же структурой. Для этих целей следует задать структуру, в которой разрешено разным группам данных занимать одну и ту же область памяти. Группа данных оформляется оператором MAP. А объединение групп и отображение на одну и ту же область памяти задается оператором UNION. Операторы имеют синтаксис: MAP объявление элементов структуры END MAP UNION map-блок map-блок [map-блок ...] END UNION Внутри объединения должно быть не менее двух map-блоков. Блок union может находиться только внутри оператора STRUCTURE. Блок map может находиться только внутри оператора UNION. Объединяемые map-блоки 91 О. В. Бартеньев. Современный ФОРТРАН должны иметь одинаковый размер. Аналогом содержащей объединения структуры, например в Паскале, является запись с вариантными полями. Пример: structure sam union map character*20 string end map map integer*2 number(10) end map end union end structure 3.9.8. Итоговые замечания Производный тип данных (структуру), если не нужно создавать объединения, следует вводить оператором TYPE ... END TYPE, используя затем для объявления записи (переменной производного типа) оператор TYPE. Компонентом записи, наряду с простыми переменными, могут быть и массивы и другие записи. Записи, наряду со строками и массивами, относятся к составным переменным. Имеющие ранг 0 записи являются скалярами. Можно так же, как и в случае данных встроенных типов, объявить записи-массивы. Запись считается неопределенной, если не определен хотя бы один ее компонент. Инициализация записи, создание записи-константы, присваивание записи значения выполняются посредством конструктора структуры, который позволяет определить или изменить значение всех компонентов записи. Кроме этого, используя селектор компонента, можно менять значение одного элемента записи. Отдельные компоненты записи могут быть использованы в выражениях так же, как и другие объекты встроенных типов. Записи можно присвоить результат выражения того же типа. Запись может быть "целиком" записана как в двоичный, так и в текстовой (форматный) файл (или считана из таких файлов). Запись или массив записей могут быть использованы в качестве параметра процедуры, но при этом тип, к которому принадлежит запись, должен быть объявлен как в вызывающей программной единице, так и в вызываемой процедуре. При объявлении типа записи-параметра используется атрибут SEQUENCE. Такой же атрибут используется и при размещении записи в common-блоке. Можно создать функцию, результатом которой является запись. Используя оператор INTERFACE OPERATOR, можно перегрузить встроенную или создать новую операцию (унарную или бинарную), 92 риложение3. Организация данных операндами которой являются записи. Наличие такой возможности улучшает структуру и сокращает код программы. Используя оператор INTERFACE ASSIGNMENT, можно задать присваивание, в котором компонентам переменной производного типа по определенным правилам присваивается результат выражения другого типа. 3.10. Целочисленные указатели Для доступа к занимаемой переменными памяти, а также к свободной памяти ЭВМ в CVF и FPS используются целочисленные указатели. Целочисленный указатель - это переменная, содержащая адрес некоторой ячейки памяти и связанная с некоторой переменной, называемой адресной переменной. Целочисленный указатель имеет 3 компонента: собственно указатель, связанную с ним адресную переменную и объект- адресат, адрес которого содержит указатель. Объектом-адресатом может также быть и ячейка выделенной функцией MALLOC памяти. Задание указателя выполняется в два этапа. Первоначально указатель связывается с адресной переменной. Это выполняется посредством оператора POINTER, имеющего синтаксис POINTER(pointer, varname) [(pointer, varname)]... Пример: real var pointer(p, var) ! p - указатель; var - адресная переменная Целочисленный указатель всегда имеет тип INTEGER(4) и не должен объявляться явно. Связываемая с ним адресная переменная (скалярная или массив) может быть любого типа. Оператор POINTER должен располагаться в разделе объявлений программной единицы. На втором этапе в указатель устанавливается адрес некоторого объекта или ячейки памяти. В первом случае это выполняется функцией LOC, во втором - MALLOC. Например: real var(5), a(5) pointer(p, var) p = loc(a) Адресная переменная через указатель связана с областью памяти, адрес которой установлен в указатель, и обладает двумя свойствами: • устанавливаемые в адресную переменную значения размещаются по хранящемуся в указателе адресу и, таким образом, передаются объекту-адресату; • данные, хранящиеся по содержащемуся в указателе адресу, передаются в связанную с ним адресную переменную. Пример первого свойства адресной переменной: 93 О. В. Бартеньев. Современный ФОРТРАН real(4) a(5) /5*0.0/, var(5), wa pointer(p, var) (p2, wa) ! Объявим два указателя p и pa и свяжем первый integer k ! с адресной переменной var, а второй - с wa p = loc(a) ! Установим указатель p на начало массива a var(2) = 0.5 ! Устанавливает также и в a(2) значение 0.5 print '(10f5.1)', a ! .0 .5 .0 .0 .0 var = -1.3 ! Все элементы массива a равны -1.3 print '(10f5.1)', a !-1.3 -1.3 -1.3 -1.3 -1.3 p2 = loc(a) ! Установим указатель p2 на начало массива a do k = 1, 5 wa = float(k) ! В a(k) устанавливается значение FLOAT(k) p2 = p2 + 4 ! Тип a - REAL(4), поэтому для перехода к следующему end do ! элементу массива a выполним p2 = p2 + 4 print '(10f5.1)', a ! 1.0 2.0 3.0 4.0 5.0 end Пример использования указателя с символьным типом данных. Заменить в заданной строке каждый нечетный символ на букву b. character(20) :: st = '1#2#3#4#5#6#7#8#9#', ch*1 pointer(p, ch) integer p0 p0 = loc(st) ! Установим в p0 адрес начала строки st do p = p0, p0 + len_trim(st), 2 ch = 'b' ! Каждый символ строки занимает 1 байт, поэтому end do ! используем шаг 2 для перемещения по нечетным символам print *, st ! b#b#b#b#b#b#b#b#b# end Пример второго свойства адресной переменной: real(4) a(5) /1.0, 2.0, 3.0, 4.0, 5.0/, wa pointer(p, wa) integer p0 ! В адресную переменную wa передается содержимое p0 = loc(a) ! области памяти, которую адресует указатель p print '(10f5.1)', (wa, p = p0, p0 + 19, 4) ! 1.0 2.0 3.0 4.0 5.0 end ! Указатель p использован в качестве параметра цикла Указатель может быть использован в качестве фактического параметра процедуры, в которой может быть изменено его значение. В общем случае целочисленный указатель может появляться везде, где могут использоваться целочисленные переменные. Следует только помнить, что указатель содержит адрес объекта, и следить за тем, чтобы после всех изменений значения указателя он адресовал нужный объект или элемент этого объекта. Попытка адресации защищенной области памяти приводит к ошибке выполнения. |