современный фортран , Бортеньев. О. В. Бартеньев Современный Фортран
Скачать 2.24 Mb.
|
104 риложение3. Организация данных Пример: real, pointer, dimension(:) :: a, a2 interface subroutine pab(b, b2) real, pointer :: b(:), b2(:) end end interface ! При вызове процедуры состояние привязки call pab(a, a2) ! формального параметра передается фактическому print '(10f5.1)', a ! 1.0 2.03.0 4.05.0 print '(10f5.1)', a2 ! .0 .0 .0 .0 .0 print *, associated(a), associated(a2) ! T T end subroutine pab(b, b2) real, pointer, dimension(:) :: b, b2 integer k real, target, save :: c(5) real, target, automatic :: c2(5) c = (/ (1.0*k, k = 1, 5) /) c2 = (/ (1.0*k, k = 1, 5) /) b => c ! После выхода из-за отсутствия у c2 атрибута b2 => c2 ! SAVE адресат ссылки a2 будет неопределен end subroutine pab Если фактическому параметру-ссылке соответствует формальный параметр, не являющийся ссылкой, то ссылка-параметр должна быть прикреплена к адресату, который и будет ассоциирован с формальным параметром. Пример: integer, pointer :: a(:) integer, target :: c(5) = 2 a => c ! Адресат c фактического параметра-ссылки a call pab(a, size(a)) ! ассоциируется с не являющимся ссылкой ! формальным параметром b подпрограммы pab print *, a ! 7 7 7 7 7 print *, c ! 7 7 7 7 7 end subroutine pab(b, n) integer n, b(n) b = b + 5 end subroutine pab 3.11.7. Параметры с атрибутом TARGET Фактический параметр процедуры может иметь атрибут TARGET. В этом случае ссылки, прикрепленные к такому параметру, не прикрепляются к соответствующему формальному параметру, а остаются 105 О. В. Бартеньев. Современный ФОРТРАН связанными с фактическим параметром. Если же формальный параметр имеет атрибут TARGET, то любая прикрепленная к нему ссылка (если только она явно не откреплена от адресата) остается неопределенной при выходе из процедуры. К процедуре, формальный параметр которой имеет атрибут TARGET, должен быть организован явный интерфейс. Таким интерфейсом обладают внутренние и модульные процедуры, обретают его и внешние процедуры после их описания в интерфейсном блоке, расположенном в вызывающей программной единице (разд. 8.11.3). 3.11.8. Ссылки как результат функции Функция может иметь атрибут POINTER. В этом случае результатом функции является ссылка и к ней можно прикрепить другую ссылку. Такое использование функции целесообразно, если, например, размер результата зависит от вычислений в самой функции. При входе в функцию вначале ссылка-результат не определена. Внутри функции она должна быть прикреплена к адресату или определена посредством оператора NULLIFY как открепленная. Обращение к функции-ссылке можно выполнить из выражения. В этом случае адресат ссылки-результата должен быть определен и его значение будет использовано при вычислении выражения. Также функция-ссылка может быть ссылочным компонентом конструктора структуры. Тема "ссылки" тесно связана с темой "массивы", поэтому дальнейшее рассмотрение ссылок мы отложим до следующей главы. Пример. Получить массив a из неотрицательных чисел массива c. integer, pointer :: a(:) integer :: c(10) = (/ 1, -2, 2, -2, 3, -2, 4, -2, 5, -2 /) a => emi(c) ! Прикрепляем ссылку к результату функции print *, a ! 1 2 3 4 5 ! Использование функции-ссылки в выражении print *, 2 * emi(c) ! 2 4 6 8 10 contains ! Внутренняя процедура обладает function emi(c) ! явно заданным интерфейсом integer, pointer :: emi(:) ! Результатом функции является ссылка integer c(:), i, k ! c - перенимающий форму массив k = count(c >= 0) ! k - число неотрицательных элементов allocate(emi(k)) ! Прикрепляем ссылку к адресату emi = pack(c, mask = c >= 0) ! Заносим в ссылку неотрицательные end function emi ! элементы массива c end Замечания: 1. Если функцию emi оформить как внешнюю, то в вызывающей программе потребуется блок с интерфейсом к этой функции, поскольку, во-первых, она 106 риложение3. Организация данных возвращает ссылку, а, во-вторых, ее формальным параметром является перенимающий форму массив. 2. Встроенные функции COUNT и PACK приведены в разд. 4.12.1 и 4.12.4.2. 107 4. Массивы Массив - это именованный набор из конечного числа объектов одного типа. Объектами (элементами) массива могут быть данные как базовых, так и производных типов. Используя атрибут PARAMETER, можно задать массив-константу. В отличие от простых переменных, предназначенных для хранения отдельных значений, массив является составной переменной. Также к составным относятся объекты символьного и производного типов. Массивы, так же как и объекты производного типа, обеспечивают доступ к некоторому множеству данных при помощи одного имени, которое называется именем массива. Также имя массива используется для обеспечения доступа к элементу или группе элементов (сечению) массива. Массивы могут быть статическими и динамическими. Под статические массивы на этапе компиляции выделяется заданный объем памяти, которая занимается массивом во все время существования программы. Память под динамические массивы выделяется в процессе работы программы и при необходимости может быть изменена или освобождена. К динамическим массивам относятся массивы-ссылки, размещаемые и автоматические массивы. Последние могут появляться только в процедурах. Память под массивы-ссылки выделяется либо в результате выполнения оператора ALLOCATE, либо после прикрепления ссылки к уже размещенному объекту-адресату. Размещаемые массивы получают память только после выполнения оператора ALLOCATE. 4.1. Объявление массива Массив характеризуется числом измерений, которых может быть не более семи. Число измерений массива называется его рангом. Число элементов массива называется его размером. Также массив характеризуется формой, которая определяется его рангом и протяженностью (экстентом) массива вдоль каждого измерения. Оператор real b(2, 3, 10) объявляет массив b ранга 3. Размер массива равен 2*3*10 = 60. Форма массива - (2, 3, 10). Каждая размерность массива может быть задана нижней и верхней границей, которые разделяются двоеточием, например: real c(4:5, -1:1, 0:9) Ранг, форма и размер массивов b и c совпадают. Такие массивы называются согласованными. Нижняя граница и последующее двоеточие при объявлении массива могут быть опущены, тогда по умолчанию нижняя граница принимается 108 4. Массивы равной единице. Объявление массива выполняется при объявлении типа либо операторами DIMENSION, ALLOCATABLE и POINTER. Также массив можно объявить в операторе COMMON. Приведем различные способы объявления статического одномерного массива целого типа из 10 элементов. Можно использовать оператор объявления типа: integer a(10) ! или a(1:10) Зададим границы в виде константного выражения (что рекомендуется): integer, parameter :: n = 10 integer a(1:n) Используем теперь атрибут DIMENSION: integer, dimension(10) :: a а затем оператор DIMENSION: integer a dimension a(10) Приведенные объявления статического одномерного массива определяют массив a из 10 объектов (элементов) с именами a(1), a(2), ..., a(10). Схема расположения элементов массива a в памяти компьютера приведена на рис. 4.1. Ячейка памяти a(1) a(2) . . . a(10) Рис. 4.1. Расположение элементов массива в памяти ЭВМ Запись a(i) отсылает нас к i-му элементу массива a. Переменную i называют индексной переменной или просто индексом. Динамический одномерный массив можно объявить, применяя операторы ALLOCATABLE или POINTER: real a, b allocatable a(:) ! Измерения динамического массива pointer b(:) ! задаются двоеточием (:) или атрибуты ALLOCATABLE или POINTER: real, allocatable :: a(:) real, pointer :: b(:) При объявлении статического массива может быть выполнена его инициализация: integer a(10) /1, 2, 3, 4, 4, 4, 5, 5, 5, 5/ или с использованием коэффициента повторения: 109 О. В. Бартеньев. Современный ФОРТРАН integer a(10) /1, 2, 3, 3*4, 4*5/ или с использованием оператора DATA: integer a(10) data a /1, 2, 3, 3*4, 4*5/ или с использованием конструктора массива: integer :: a(10) = (/ 1, 2, 3, 4, 4, 4, 5, 5, 5, 5 /) или с использованием в конструкторе массива циклического списка: integer :: i, j, a(10) = (/ 1, 2, 3, (4, i = 4, 6), (5, i = 7, 10) /) Во всех приведенных примерах после инициализации массива a a(1) = 1, a(2) = 2, a(3) = 3, a(4) = 4, a(5) = 4, a(6) = 4, a(7) = 5, a(8) = 5, a(9) = 5, a(10) = 5. При инициализации необходимо, чтобы число констант в списке значений равнялось числу элементов массива. Используя в операторе DATA неявный цикл, можно инициализировать часть массива. Одним оператором может быть выполнено объявление более одного массива. Например, оператор real b(-3:3) /7*1.0/, g2(6:8) ! Массив g2 не определен объявляет два одномерных массива - массив b из семи элементов с именами b(-3), b(-2), ..., b(3) и массив g2 из трех элементов с именами g2(6), g2(7), g2(8). Все элементы массива b равны 1.0. Пример. Найти сумму элементов одномерного массива. real b(-3:5) /1.1, 2.2, 3.3, 4.4, 5.5, 4*7.8/, s s = 0.0 do k = -3, 5 s = s + b(k) end do write(*, *) ' s = ', s ! s = 47.7 ! Та же задача решается с применением встроенной функции SUM: write(*, *) ' s = ', sum(b) ! s = 47.7 Аналогично выполняется объявление двумерного массива: integer b(2, 4) ! Статический массив из восьми элементов real, pointer :: c(:, :) ! Динамический массив-ссылка Первое объявление определяет двумерный массив b из восьми элементов с именами b(1,1), b(2,1), b(1,2), b(2,2), b(1,3), b(2,3), b(1,4), b(2,4). Двумерный массив b(1:2, 1:4) можно представить в виде таблицы (рис. 4.2), содержащей 2 строки и 4 столбца. 110 4. Массивы j 1 2 3 4 i 1 b(1, 1) b(1, 2) b(1, 3) b(1, 4) 2 b(2, 1) b(2, 2) b(2, 3) b(2, 4) Рис. 4.2. Представление двумерного массива в виде таблицы Память компьютера является одномерной, поэтому элементы двумерного массива b расположены в памяти в линейку так, как это показано на рис. 4.3. b(1, 1) b(2, 1) b(1, 2) b(2, 2) b(1, 3) b(2, 3) b(1 ,4) b(2, 4) 1 -1 2 -2 3 -3 4 -4 Ячейка памяти Рис. 4.3. Расположение элементов двумерного массива в памяти ЭВМ Запись b(i, j) отсылает нас к j-му элементу массива в его i-й строке, где i и j - индексы массива b. Во многих языках программирования, например в СИ, элементы двумерного массива располагаются в памяти ЭВМ по строкам, в Фортране – по столбцам, т. е. быстрее изменяется первый индекс массива. В более общем случае в Фортране для многомерного массива при размещении его элементов в памяти ЭВМ закономерность изменения индексов можно отобразить вложенным циклом (на примере трехмерного массива a(2, 4, 6): do k = 1, 6 do j = 1, 4 do i = 1, 2 ! Быстрее всего изменяется индекс i размещение в памяти элемента с именем a(i, j, k) end do end do end do Иными словами, при размещении многомерного массива в памяти ЭВМ быстрее всего изменяется самый левый индекс массива, затем следующий за ним индекс и т. д. Это обстоятельство следует учитывать при В/В и инициализации многомерного массива. В случае двумерного массива перечисление значений приведет к инициализации по столбцам (соответствие элемент - значение показано на рис. 4.3): integer b(2, 4) / 1, -1, 2, -2, 3, -3, 4, -4 / Инициализация по строкам может быть выполнена оператором DATA: data ((b(i, j), j = 1, 4), i = 1, 2) /1, -1, 2, -2, 3, -3, 4, -4/ 111 О. В. Бартеньев. Современный ФОРТРАН или в конструкторе массива при надлежащем применении функции RE- SHAPE: integer :: b(2, 4) = reshape((/ & 1, -1 2 -2 & 3 -3 4 -4 /), shape = (/ 2, 4 /), order = (/ 2, 1 /)) Пример. Найти произведение положительных элементов двумерного массива. real b(-2:1, 6:8) /1.1, 2.2, 3.3, 4.4, 5.5, 7*-1.1/, p integer i, j p = 1.0 do j = 6, 8 do i = -2, 1 if(b(i, j) > 0) p = p * b(i, j) end do end do write(*, *) ' p = ', p ! p = 193.2612 ! Для вычисления произведения можно применить встроенную функцию PRODUCT: p = product(b, mask = b > 0.0) write(*, *) ' p = ', p ! p = 193.2612 Замечание. Порядок размещения элементов многомерного массива следует учитывать при организации вложенных циклов. Так, цикл do j = 6, 8 do i = -2, 1 ! Перебираем элементы очередного столбца массива if(b(i, j) > 0) p = p * b(i, j) end do end do отработает быстрее, чем цикл do i = -2, 1 do j = 6, 8 ! Неестественный порядок перебора элементов if(b(i, j) > 0) p = p * b(i, j) ! массива b end do end do Это объясняется тем, что в первом случае во внутреннем цикле обеспечивается естественная и поэтому более производительная последовательность доступа к элементам массива. Индексом массива может быть целочисленное выражение: real :: b(5, 10) = 5.1 real :: a(5, 5), c(30), r = 7.0 c(int(r)*2 + 1) = 2.0 ! Индекс массива - целочисленное выражение a(1, 2) = b(int(c(15)), int(sqrt(r))) write(*, *) a(1, 2), b(2, 2) ! 5.100000 5.10000 112 4. Массивы При объявлении массива помимо задающих динамические массивы атрибутов ALLOCATABLE и POINTER и атрибута задания формы массива DIMENSION могут быть использованы атрибуты INTENT, OPTIONAL, PARAMETER, PRIVATE, PUBLIC, SAVE и TARGET. 4.2. Массивы нулевого размера Массив, как, впрочем, и строка, может иметь нулевой размер. Всегда, когда нижняя граница превосходит соответствующую верхнюю границу, массив имеет размер 0. Например: real b2(0, 15), d(5, 20, 1:-1) print *, size(b2), size(d) ! 0 0 Массивы нулевого размера всегда считаются определенными и при использовании подчиняются обычным правилам. 4.3. Одновременное объявление объектов разной формы При необходимости можно одним оператором объявлять объекты разной формы. Так, оператор real, dimension(10) :: a, b, b2(15), d(5, 20) объявляет массивы a и b формы (10), массив b2 формы (15) и массив d формы (5, 20). То есть приоритетом при использовании атрибута DIMEN- SION обладает явное описание формы массива. Оператор integer na, nb, a(10), d(5, 20) объявляет скаляры na и nb и массивы a и d разной формы. 4.4. Элементы массива В этом разделе мы обобщим понятие элемента массива. Массив может содержать элементы встроенных и производных типов данных: type order ! Описание заказа integer ordnum, cus_id character(15) item(10) end type ! Примеры массивов символьного и производного типа character(20) st(10) /10*'T-strings'/ ! st - массив строк type(order) cord, orders(100) ! orders - массив заказов Приведенные описания определяют массивы st, orders, cord%item и orders(k)%item (k - целое и 0 ≤ k ≤ 100). Элементы массивов являются скалярами. Примеры элементов массивов: st(7), orders(15), cord%item(7), orders(10)%item(8) 113 О. В. Бартеньев. Современный ФОРТРАН Из символьного массива можно извлечь еще один объект данных - подстроку, например: print *, st(7)(1:6) ! T-stri или character(15) itord orders = order( 2000, 455, (/ ('Item', k = 1, 10) /) ) itord = orders(10)%item(8) print *, itord(3:4) ! em Однако содержащаяся в символьном массиве подстрока по соглашению не рассматривается как элемент массива. В общем случае элемент массива - это скаляр вида частный указатель [%частный указатель...] где частный указатель есть частное имя [(список индексов)] Если частный указатель является именем массива, то он обязан иметь список индексов, например orders(10)%item(8). Число индексов в каждом списке индексов должно равняться рангу массива или массива - компонента производного типа. Каждый индекс должен быть целым скалярным выражением, значение которого лежит в пределах соответствующих границ массива или массива-компонента. 4.5. Сечение массива Получить доступ можно не только к отдельному элементу массива, но и к некоторому подмножеству его элементов. Такое подмножество элементов массива называется сечением массива. Сечение массива может быть получено в результате применения индексного триплета или векторного индекса, которые при задании сечения подставляются вместо одного из индексов массива. Индексный триплет имеет вид: [нижняя граница] : [верхняя граница] [:шаг] Каждый из параметров триплета является целочисленным выражением. Шаг изменения индексов может быть и положительным и отрицательным, но не может равняться нулю. Все параметры триплета являются необязательными. Индексный триплет задает последовательность индексов, в которой первый элемент равен его нижней границе, а каждый последующий больше (меньше) предыдущего на величину шага. В последовательности находятся все задаваемые таким правилом значения индекса, лежащие между границами триплета. Если же нижняя граница больше верхней и шаг 114 4. Массивы положителен или нижняя граница меньше верхней и шаг отрицателен, то последовательность является пустой. Пример: real a(1:10) a(3:7:2) = 3.0 ! Триплет задает сечение массива с элементами ! a (3), a(5), a(7), которые получат значение 3.0 a(7:3:-2) = 3.0 ! Элементы a(7), a(5), a(3) получат значение 3.0 При отсутствии нижней (верхней) границы триплета ее значение принимается равным значению нижней (верхней) границы соответствующего экстента массива. Так, a(1:5:2) и a(:5:2) задают одно и то же сечение массива a, состоящее из элементов a(1), a(3), a(5). Сечение из элементов a(2), a(5), a(8) может быть задано так: a(2:10:3) или a(2::3). Пример: real a(10), r /4.5/ a(2::3) = 4.0 ! Элементы a(2), a(5), a(8) получат значение 4.0 a(7:9) = 5.0 ! Элементы a(7), a(8), a(9) получат значение 5.0 a(:) = 3.0 ! Все элементы массива получат значение 3.0 a(::3) = 3.0 ! Сечение из элементов a(1), a(4), a(7), a(10) a(::int(r / 2)) = 3.0 ! Параметр триплекса - целочисленное выражение print *, size(a(4:3)) ! 0 (сечение нулевого размера) ! Функция SIZE возвращает размер массива Нижняя граница триплета не может быть меньше нижней границы соответствующего экстента массива. Так, ошибочно задание сечения a(-2:5:3) в массиве a(1:10). Верхняя граница триплета должна быть такой, чтобы последний элемент задаваемой триплетом последовательности не превышал верхней границы соответствующего экстента массива. Например, в массиве a(1:10) допустимо сечение a(3:12:5). Верхняя граница триплета всегда должна присутствовать при использовании триплета в последней размерности перенимающего размер массива (разд. 4.9.3). В случае многомерного массива сечение может быть задано посредством подстановки индексного триплета (впрочем, так же, как и векторного индекса) вместо одного или нескольких индексов, например: real a(8, 3, 5) a(1:4:2, 2:3, 4) = 4.0 В заданном сечении в первом измерении индекс может принимать значения 1 и 3, во втором - 2 и 3, а в третьем - только 4. Таким образом, сечение обеспечивает доступ к элементам a(1, 2, 4), a(1, 3, 4), a(2, 2, 4) и a(2, 3, 4). Поскольку в третьем измерении сечения индекс фиксирован, то сечение является двумерным массивом с формой (2, 2). |