Лекция 9 ПРОИЗВОДНЫЕ ТИПЫ Производные типы, зачем ? ! данные N частиц real x(N) real y(N) real z(N) logical status(N) real temperature(N) real pressure(N) integer color(N) При сортировке используются перестановки элементов. Перестановка требует 3 операции присваивания. tmp = x(12) x(12) = x(15) x(15) = tmp Для перестановки двух частиц потребуется записать 21(!) операции присваивания. Производные типы, зачем ? Лучше объединить типы под одним "общим типом“. type particle real x real y real z logical status real temperature real pressure integer color end type particle ! координаты поля ! физические ! параметры ! цвет type (particle) M(N), tmp ... tmp = M(12) ! 3 присваивания M(12) = M(15) M(15) = tmp Оператор type Объявляет новый тип данных, группируя под одним именем существующие типы. type имя типы данных contains процедуры привязанные к типу end type имя Доступ к полям Операция " . " или " % ". program prog type NewType integer A real B character C logical D end type NewType type (NewType) PS PS.A = 35; PS%A = 25; PS.B = 3.14; PS.C = 'E'; PS.D = .FALSE. PS%B = 5.67; PS%C = 'Q'; PS%D = .TRUE. end Задание начальных значений для каждого поля лучше с использованием конструктора. Конструктор производного типа Конструктор – функция с именем производного типа. Параметры функции - поля производного типа. program prog type NewType integer A real B character C logical D end type NewType конструктор type (NewType) :: PS0 = NewType(1, 0.32, 'Z', .FALSE.) type (NewType) PS1 type (NewType), allocatable :: PS2 PS1 = NewType(5, 3.5, 'Q', .TRUE.) allocate(PS2); PS2 = NewType(8, 1.2, 'F', .TRUE.) ! ИЛИ allocate(PS2, source = NewType(8, 1.2, 'F', .TRUE.)) write(*,*) PS2 ! вывод значений всех полей end Иерархия типов type person character(128) fio integer age end type person type firm type (person) people(1000) character(128) name integer money end type firm type (firm) FM FM.money = 100000 ! доступ к полям FM.name = 'Siberia' FM.people(1).fio = 'Ivanov S.K.' FM.people(1).age = 35 Расширение типа, extends Тип CHILD наследует поля типа PARENT type PARENT ! родитель integer A real B end type PARENT type, extends (PARENT) :: CHILD ! потомок character C end type CHILD type (CHILD) pas1, pas2 pas1 = CHILD(PARENT(1,2.0),'A') pas2 = CHILD(1,2.0,'A') Оператор class Class объявляет полиморфную переменную. Если полиморфная переменная не является формальным параметром процедуры, то используются атрибуты allocatable или pointer. class (имя производного типа), allocatable :: имя type PARENT integer A real B end type PARENT type, extends (PARENT) :: CHILD ! наследуем тип PARENT character C end type CHILD type (CHILD), allocatable :: CL1 class (CHILD), allocatable :: CL2 allocate(CL1, source = CHILD(1,3.0,'Q')) allocate(CL2, source = CHILD(1,3.0,'Q')) Переменная CL2 имеет больше возможностей. Оператор class Полиморфная переменная объявленная родительским типом может "принимать" все дочерние типы. program prog type PARENT integer A real B end type PARENT type, extends (PARENT) :: CHILD_A character C end type CHILD_A type, extends (PARENT) :: CHILD_B logical D end type CHILD_B type, extends (CHILD_A) :: CHILD_CHILD_A complex E end type CHILD_CHILD_A Оператор class class (PARENT), pointer type type type type PAR PAR PAR PAR :: PAR (PARENT), target :: P (CHILD_A), target :: CA (CHILD_B), target :: CB (CHILD_CHILD_A), target :: CCA => => => => P CA CB CCA ! ! ! ! стал родителем теперь потомок А изменился на потомка B теперь потомок потомка А end CHILD_A PARENT CHILD_B CHILD_CHILD_A Конструкция select type Как определить какой тип имеет полиморфная переменная ? Select type позволяет выполнить блок операторов в зависимости от динамического типа полиморфной переменной. select type (переменная) type is (имя типа) class is (имя типа) class default end select Оператор select type Схема выполнения Находится и выполняется блок type is Если не найден type is, то находится и выполняется class is. Если найдено соответствие нескольким блокам class is, то выбирается ближайший родитель. Если не найден ни один блок выбирается class default. Оператор select type ... type (PARENT), target :: P type (CHILD_CHILD_A), target :: CCA PAR => CCA ! потомок потомка А select type (PAR) class is (PARENT) write (*,*) "PARENT" class is (CHILD_A) write(*,*) "CHILD" ! выбирается ближайший родитель class default write(*,*) "default...." end select end CHILD_A PARENT CHILD_B CHILD_CHILD_A Оператор class(*) Неограниченно полиморфная переменная принимает любые типы type T1 integer index real val end type T1 type T2 logical status character symbol character(10) name end type T2 complex(16), target :: CMP type (T1), target :: PT1 type (T2), target :: PT2 class (*), pointer PAR => PT1 PAR => PT2 PAR => CMP :: PAR ! неограниченно полиморфная ! сейчас типа T1 ! теперь типа T2 ! затем комплексный тип Процедурные указатели procedure(proc), pointer :: p1 => null() Procedure описывает процедурный указатель, позволяет добавлять процедуры в созданный тип. type NewType integer a real b contains procedure proc1 procedure :: proc2 => other_pr end type NewType ... call A.proc1(a,b) Атрибуты pass и nopass Используются для процедур привязанных к производному типу по имени. pass позволяет получить доступ к переменной, посредством которой вызывалась процедура (по умолчанию). Вызывающая переменная записывается в процедуре, первым параметром и должна быть объявлена оператором class. При вызове процедуры данный параметр опускается. nopass отменяет доступ к вызывающей переменной. Процедуры привязанные к типу module algebra type, public :: vector real x1, y1, x2, y2 contains procedure, public, pass :: length procedure, nopass :: info end type vector CONTAINS subroutine info() write(*,*) "I'am VECTOR" end subroutine info integer function length(vc) ! атрибут pass class(vector) vc length = sqrt((vc.x1-vc.x2)**2 + (vc.y1-vc.y2)**2) end function length end module algebra Процедуры привязанные к типу program prog use algebra class (vector), allocatable :: VEC allocate(VEC, source = vector(0.0,0.0,3.0,4.0)) call VEC.info() write(*,*) VEC.length() ! формальный параметр отсутствует ! однако при описании объявлен deallocate(VEC) end Завершающие процедуры Оператор final объявляет процедуры (деструкторы), которые выполняются при удалении ранее размещенных в памяти элементов type NewType ... contains final :: finish end type NewType Атрибут private Используется для задания отдельных полей производных типов в модулях. Доступ к приватной части происходит при помощи public-процедур. module MyModule ... type NewType integer A real, private :: B integer C real, private :: D end type NewType ... end module MyModule Атрибут private (Пример) module MyModule type NewType integer A real, private :: B contains procedure :: SetParamB end type NewType contains subroutine SetParamB(T,newvalue) class(NewType) T real newvalue if (newvalue < 0) then write(*,*) "Error in parameter B, must be >=0" T.B = 0 else T.B = newvalue end if end subroutine end module Атрибут private (Пример) program prog use MyModule class(NewType), allocatable :: nw allocate(nw) nw.A = 1000 !nw.B = 4.5 ! ошибка доступа call nw.SetParamB(4.5) call nw.SetParamB(-9.4) ! некорректные данные end Перегрузка операций Набросок модуля арифметики длинных чисел. module long type LongNumbe integer(1) val(length) integer total end type LongNumber ! цифры ! количество contains subroutine asgn(n,val) ! присваивание ! операторы end subroutine asgn function plus(n1,n2) ! операция сложение и другие ! операторы end function plus subroutine PrintLong(n) ! вывод числа ! операторы end subroutine PrintLong end module long Перегрузка операций Набросок вызывающей программы program prog use long type (LongNumber) a, b, c, d call asgn(a,"2823892839283923837483485555555") call asgn(b,"92882746") call asgn(c,"2038493849300000") !---- хотим найти выражение ! d = a*(b+c)+c*(a+b)+b d = plus(plus(umn(a,plus(b,c)),umn(c,plus(a,b))),b) ! очень громоздкая запись ! осложнение если будет много операций call PrintLong(d) end Перегрузка операций Перегрузка операции присваивания interface assignment (=) module procedure asgn ! имя процедуры end interface Перегрузка операции сложения, умножения и др. interface operator (+) module procedure plus ! имя процедуры end interface Замена имени процедуры на знак операции. Перегрузка операций Унарная операция - функция с одним входным параметром имеющего вид связи IN. Двуместная операция - функция с двумя параметрами имеющими вид связи IN. Нельзя изменять тип встроенной операции. (например '*' оформить как унарную). Процедура, задающая '=' должна быть подпрограммой с двумя параметрами. 1-й вид связи OUT или INOUT (левая часть), 2-й параметр IN (правая часть). Задаваемые операции Вводятся аналогично унарным и двуместным операциям. Имя операции задаётся по общим правилам. В выражениях операция ограничивается точками. interface operator (.PLUS.) module procedure plus end interface ... SUMMA = A.PLUS.B Приоритет операций унарная перегруженная или задаваемая операция арифметические операции символьная операция конкатенация операции отношения логические операции задаваемая или перегруженная бинарная операция *Задание* Создать модуль для работы с длинными числами. Реализовать операции присваивания, сложения и вывода длинных целых чисел.