Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
современный фортран , Бортеньев.pdf
Скачиваний:
242
Добавлен:
26.03.2015
Размер:
2.34 Mб
Скачать

О. В. Бартеньев. Современный ФОРТРАН

3.9.2.2. Присваивание значений компонентам записи

Продолжим работу с переменной cur_ord только что введенного производного типа order:

! Присвоим значение отдельному компоненту записи

cur_ord%cus_id = 1300

! Изменим код покупателя

! Присвоим значение компоненту записи - элементу массива:

cur_ord%item(2)%color = 'blue'

! Изменим цвет второй вещи заказа

! Присвоим значение всему массиву - компоненту записи: cur_ord%item%color = 'none'

Если компонентом записи является массив, то для его определения можно использовать конструктор массива (разд. 4.6), например:

type vector

 

 

 

integer n

 

 

 

integer vec(10)

! j-му элементу массива vec присвоим значение j * 2

end type

type(vector) :: vt = vector(5, (/ (j * 2, j = 1, 10) /))

print *, vt.n, vt.vec(2)

!

5

4

3.9.2.3. Задаваемые присваивания записей

Можно изменить значение переменной производного типа, присвоив ей значение другой переменной, константы, конструктора или выражения того же типа. Однако область действия встроенного присваивания можно расширить, связав с оператором присваивания (=) посредством блока INTERFACE ASSIGNMENT модульную или внешнюю подпрограмму, которая будет вызываться каждый раз, когда в программе встречается заданное присваивание (разд. 8.12.2).

3.9.3. Выражения производного типа

Если не принять специальных мер, нельзя применять встроенные операции в выражениях с записями. Нельзя, например, сложить две записи, применяя встроенную операцию сложения. Мерой, позволяющей распространить встроенную операцию на производный тип, является перегрузка операций (разд. 8.12.2). Для задания (перегрузки) операции создается функция, которая при помощи интерфейсного блока связывается с задаваемой операцией. Эта функция вызывается каждый раз, когда встречается заданная операция, и возвращает для последующего использования в выражении результат этой операции.

Пример. Зададим операцию умножения числа на запись.

module deta

! Определим производный тип pair

type pair

! в модуле deta

real x, y

 

end type pair

 

end module

 

84

 

 

 

риложение3. Организация данных

program paw

! Получаем доступ к типу pair

use deta

interface operator(*)

! К задающей операцию внешней функции

function mu(a, b)

! необходимо явно описать интерфейс

use deta

 

 

 

type(pair) mu

 

 

 

type(pair), intent(in) :: b

! Вид связи параметров задающей

real, intent(in) :: a

! операцию функции должен быть IN

end function

 

 

 

end interface

 

 

 

type(pair) :: pt1 = pair(2.0, 2.0), pt2

 

 

pt2 = 2.0 * 2.5 * pt1

! Первая операция умножения встроенная,

 

! вторая - перегруженная

print *, pt2

!

10.000000

10.000000

end program paw

 

 

 

function mu(a, b)

! Функция будет вызываться каждый раз,

use deta

! когда первым операндом операции *

type(pair) mu

! будет выражение типа REAL,

type(pair), intent(in) :: b

! а вторым - выражение типа pair

real, intent(in) :: a

 

 

 

mu.x = a * b.x

 

 

 

mu.y = a * b.y

 

 

 

end function

 

 

 

3.9.4. Запись как параметр процедуры

Если запись используется в качестве параметра процедуры и ее тип повторно определяется в процедуре оператором TYPE ... END TYPE, то при его определении и в вызывающей программной единице, и в процедуре необходимо задать атрибут SEQUENCE. Это обеспечит одинаковое расположение компонентов записи в памяти. (Порядок размещения компонентов в памяти определяется на этапе компиляции.) Если в определении производного типа встречаются иные производные типы, то они тоже должны иметь атрибут SEQUENCE.

Если производный тип определяется в модуле, атрибут SEQUENCE избыточен: модули компилируются отдельно и, следовательно, в каждой программной единице, получающей определение производного типа посредством use-ассоциирования, компоненты такой записи будут размещены в памяти одинаково.

Пример:

program gopo

! Главная программа

type point

! В главной программе и функции pval

sequence

! определен один и тот же тип point

real x, y

 

end type

 

type(point) pt

 

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 и y определяют компоненты

x = 1.0

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

! Получаем доступ к типу pair

use deta

type(pair) :: pt1 = pair(2.0, 2.0)

 

 

 

type(pair) :: pt2

! Вызов внутренней функции mu

pt2 = mu(2.5, pt1)

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

! stud - переменная типа exam

type(exam) stud

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

! rnd - случайное число типа REAL(4) (0.0 rnd < 1.0)

call random(rnd)

rmark = nint(8.5 * rnd)

! Округление

rmark = max(rmark, 2)

! Оценка не может быть менее двух

rmark = min(rmark, 5)

! Оценка не может быть более пяти

end function

 

end

 

Пояснения:

88