Программирование больших вычислительных задач на современном Фортране с использованием компиляторов Intel

Производные типы

Показывать лекцию целиком

Презентацию к лекции Вы можете скачать здесь.

Производные типы, зачем ?

! данные 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

Объявляет новый тип данных, группируя под одним именем существующие типы.

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.B = 3.14; PS.C = 'E'; PS.D = .FALSE.
  PS%A = 25;  PS%B = 5.67; PS%C = 'Q'; PS%D = .TRUE.

end
    

Задание начальных значений для каждого поля лучше с использованием конструктора.

Конструктор производного типа

Конструктор – функция с именем производного типа.

Параметры функции - поля производного типа.

Иерархия типов

Расширение типа, 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 имеет больше возможностей.

Полиморфная переменная объявленная родительским типом может "принимать" все дочерние типы.

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 (PARENT), pointer  :: PAR

  type (PARENT),   target :: P
  type (CHILD_A),  target :: CA
  type (CHILD_B),  target :: CB
  type (CHILD_CHILD_A), target :: CCA


  PAR => P   ! стал родителем
  PAR => CA  ! теперь потомок А
  PAR => CB  ! изменился на потомка B
  PAR => CCA ! теперь потомок потомка А

end
    

Конструкция 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.
  • ...
      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
        

    Оператор 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  ! неограниченно полиморфная
    
     PAR => PT1     ! сейчас типа T1
     PAR => PT2     ! теперь типа T2
     PAR => CMP     ! затем комплексный тип
        

    Процедурные указатели

    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), intent(out) :: 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
        

    Пример

    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
    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
        

    Приоритет операций

  • унарная перегруженная или задаваемая операция
  • арифметические операции
  • символьная операция конкатенация
  • операции отношения
  • логические операции
  • задаваемая или перегруженная бинарная операция
  • Задание

    Создать модуль для работы с длинными числами. Реализовать операции присваивания, сложения и вывода длинных целых чисел.

    Вернуться к учебному плану