современный фортран , Бортеньев. О. В. Бартеньев Современный Фортран
Скачать 2.24 Mb.
|
115 О. В. Бартеньев. Современный ФОРТРАН Частными случаями сечений двумерного массива являются его строки и столбцы, например: integer a(5, 8) a(3, :) = 3 ! Сечение - третья строка матрицы a(:, 4) = 7 ! Сечение - четвертый столбец матрицы Пример. В какой строке матрицы чаще встречается заданное число k. integer, parameter :: m = 3, n = 5 integer :: a(m, n), b(m), i, k = 2, km(1) a = reshape((/ 1, 2, 2, 4, 3, & 2, 4, 2, 8, 2, & -2, 2, 6, 2, 2 /), shape = (/m, n/), order = (/2, 1/)) do i = 1, m ! Запишем число вхождений k в строку i в b(i) = count(a(i, :) == k) ! массив b. Сечение a(i, :) является end do ! i-й строкой массива km = maxloc(b) print *, 'Строки в которых k = ', k, ' входит наибольшее число раз' do i = 1, m if(b(i) == b(km(1))) print *, 'Строка ', i end do end Замечание. Массив b можно сформировать, не применяя цикла: b = count(a == k, 2) ! Функция COUNT рассмотрена в разд. 4.12.1. Векторный индекс является одномерным целочисленным массивом, содержащим значения индексов, попадающих в сечение исходного массива, например: real a(20), b(10, 10) integer :: vi(3), vj(2) ! vi, vj - целочисленные массивы; vi= (/1, 5, 7/) ! используются как векторные индексы vj= (/2, 7/) ! для задания сечений массивов a и b a(vi) = 3.0 ! a(1), a(5), a(7) получат значение 3.0 b(2, vj) = 4.0 ! b(2, 2), b(2, 7) - значение 4.0 b(vi, vj) = 5.0 ! b(1, 2), b(1, 7), b(5, 2), b(5, 7), ! b (7, 2) и b(7, 2) - значение 5.0 Векторный индексв отличие от индексного триплета позволяет извлечь в сечение произвольное подмножество элементов массива. Значения индексов должны находиться в пределах границ соответствующей размерности исходного массива. Значения индексов в векторном индексе могут располагаться в произвольном порядке и могут повторяться. Например: real a(10, 8) /80 * 3.0/, b(5) b = a(3, (/5, 3, 2, 7, 2/)) 116 4. Массивы В массив b попадут значения элементов сечения массива a: a(3, 5), a(3, 3), a(3, 2), a(3, 7) и вновь a(3, 5). Сечения с повторяющимися значениями индексов не могут появляться в правой части оператора присваивания и в списке ввода оператора READ. Например, присваивание real a(10) a(/5, 3, 2, 7, 2/) = (/1.2 , 1.3, 1.4, 1.5, -1.6/) недопустимо, поскольку 1.4 и -1.6 не могут одновременно храниться в a(2). Размер сечения равен нулю, если векторный индекс имеет нулевой размер. Сечение массива с векторным индексом не может быть внутренним файлом, адресатом ссылки. Если сечение массива с векторным индексом является фактическим параметром процедуры, то оно рассматривается как выражение и соответствующий формальный параметр должен иметь вид связи INTENT(IN). При использовании заданного векторным индексом сечения в качестве фактического параметра в процедуре создается копия этого сечения, которую и адресует соответствующий формальный параметр. Сечение массива (заданное индексным триплетом или векторным индексом) сохраняет большинство свойств массива и может быть, в частности, использовано в качестве параметра встроенных функций для массивов, элементных и справочных функций и пользовательских процедур. Пример. Найти сумму положительных элементов главной диагонали квадратной матрицы. integer, parameter :: n = 10 integer :: a(n, n) /100 * 3.0/, i integer :: b(n * n) integer :: v(n) = (/ (i + n * (i - 1), i = 1, n) /) a(9, 9) = -1 ! v - векторный индекс, содержащий b=reshape(a, shape = (/100/)) ! номера элементов главной диагонали ! массива a; b(v) - сечение массива b print *, sum(b(v), mask=b(v)>0) ! 27 Использование сечений позволяет более эффективно решать задачи, для которых раньше применялись DO-циклы (разд. 7.5). Пример. Поменять порядок следования элементов массива. integer :: i, a(10) = (/ (i, i = 1, 10) /) a = a(10:1:-1) print '(10i3)', a ! 10 9 8 7 6 5 4 3 2 1 Сечение, помимо рассмотренных случаев, может быть взято у массивов производных типов, а также содержать массивы - компоненты структур и подстроки символьных массивов. Общий вид сечения массива: 117 О. В. Бартеньев. Современный ФОРТРАН частный указатель [%частный указатель ...] ... [(диапазон подстроки)] где частный указатель есть частное имя [(список индексов сечения)] Число индексов сечения в каждом списке должно равняться рангу массива или массива - компонента структуры. Каждый индекс сечения должен быть либо индексом, либо индексным триплетом, либо векторным индексом, например: real a(8, 5, 5) a(4, 1:4:2, (/2, 5/)) = 4.0 ! 4 - индекс; 1:4:2 - индексный триплет; ! (/2, 5/)) - векторный индекс Частный указатель ненулевого ранга определяет ранг и форму сечения. Размер сечения равен нулю, если хотя бы один из экстентов частного указателя равен нулю. Диапазон подстроки может присутствовать, только если последний частный указатель относится к символьному типу и является скаляром или имеет список индексов сечения. Пример сечения, состоящего из подстрок массива: character(len = 20) st(10) /10*'Test String'/ print *, st((/1, 3, 10/))(5:8) ! Str Str Str print *, st(2:6:2)(5:8) ! Str Str Str Сечение массива, имя которого заканчивается именем компонента структуры, также является компонентом структуры. Пример сечений, содержащих массивы - компоненты структур: type order ! Описание заказа: integer(4) ordnum, cus_id ! номер заказа, код покупателя character(15) item(10) ! список вещей заказа end type type(order) cord, ords(100) ! ords - массив заказов cord%item(2:6) = 'dress' ords(5:7:2)%item(7) = 'tie' ords(9)%item((/1, 8, 9/)) = 'blazer' ords(9)%item((/8, 9/))(8:9) = '20' print *, cord%item(3), ords(5)%item(7) ! dress tie print *, ords(9)%item(1), ords(9)%item(8) ! blazer blazer 20 Образуемое из массивов - компонентов структур сечение не может содержать более одного нескалярного объекта. Так, попытка создать сечение вида ords(5:7:2)%item((/ 1, 8, 9 /)) = 'none' вызовет ошибку компиляции. Частное имя справа от частного указателя не должно иметь атрибут POINTER. Так, нельзя задать сечение list(1:15:2)%ival в таком примере: 118 4. Массивы type wip real val integer, pointer :: ival end type wip type(wip) elem, list(20) allocate(elem%ival) ! Правильно allocate(list(5)%ival) ! Правильно allocate(list(1:15:2)%ival) ! Ошибка 4.6. Присваивание массивов Как было показано выше, массив может быть определен при инициализации в операторах объявления типа или в операторе DATA. Также значения элементов массива можно изменить, присвоив массиву или его сечению результат выражения. Операндом такого выражения может быть конструктор массива. Например: real b(5), pi /3.141593/ integer a(5) b = tan(pi / 4) ! Присваивание значения выражения всему массиву b(3) = -1.0 ! Присваивание значения третьему элементу массива write(*,'(7f5.1)') b ! 1.0 1.0 -1.0 1.0 1.0 a = 2 * (/ 1, 2, 3, 4, 5 /) ! Конструктор массива как операнд выражения write(*, *) a ! 2 4 6 8 10 Конструктор массива задает одномерный массив и имеет вид: (/ список-значений /) Пробелы между круглой скобкой и слешем не допускаются. Список-значений может содержать последовательность скаляров, неявных циклов и массивов любого ранга. Значения в списке разделяются запятыми и должны иметь одинаковый тип и разновидность типа. Каждое значение списка может быть результатом выражения. Неявный цикл конструктора массива имеет вид: (выражение | неявный цикл, dovar = start, stop [, inc]) dovar - целочисленная скалярная переменная (параметр цикла). start, stop, inc - целочисленные константные выражения, определяющие диапазон и шаг изменения dovar. Если inc отсутствует, то шаг устанавливается равным единице. Неявный цикл добавляет в список значений MAX(stop - start + INT(inc / inc), 0) элементов. Выражение может содержать dovar. Возможна организация вложенных неявных циклов. Если в списке появляется многомерный массив, то его значения берутся в порядке их размещения в памяти ЭВМ. Конструктор массива позволяет сгенерировать значения одномерного массива. При задании значений 119 О. В. Бартеньев. Современный ФОРТРАН многомерного массива следует получить при помощи конструктора одномерный массив необходимого размера, а затем применить функцию RESHAPE и вписать данные в заданную форму. Число элементов в списке- значений должно совпадать с размером массива. Пример 1. Элементы списка - массивы и простой скаляр. integer b(7), c(2, 3), i, j integer a(3) / 3, 2, 1 / b = (/ a, a, mod(a(1), 2) /) ! В списке одномерный массив и скаляр write(*,'(10i3)') b ! 3 2 1 3 2 1 1 data ((c(i, j), j = 1, 3), i = 1, 2) /3*1, 3*2/ b = (/ c, -1 /) ! В списке двумерный массив и скаляр write(*, '(10i3)') b ! 1 2 1 2 1 2 -1 Пример 2. Элементы списка - неявные циклы. integer a(5), i, k real :: r(7) real, parameter :: pi = 3.141593 logical fl(10) a = (/ (i, i = 1, 5) /) write(*, *) a ! 1 2 3 4 5 r = (/(cos(real(k) * pi / 180.0), k = 1, 14, 2)/) write(*, '(10f5.1)') r ! 1.0 1.0 1.0 1.0 1.0 1.0 1.0 fl = (/(.true., k = 1, 5), (.false., k = 6, 10)/) write(*, *) fl ! T T T T T F F F F F Пример 3. Присваивание значений двумерному массиву. integer a(5, 2), i, j ! Элементы списка в конструкторе массива b - integer b(3, 4) ! вложенные неявные циклы a = reshape(source = (/ (2*i, i = 2, 11) /), shape = (/ 5, 2 /)) b = reshape((/ ((i*j, i = 1,3), j = 3, 6) /), shape = (/ 3, 4 /)) write(*, '(10i3)') a write(*, '(4i3)') ((b(i, j), j = 1, 4), i = 1, 3) Результат: 4 6 8 10 12 14 16 18 20 22 3 4 5 6 6 8 10 12 9 12 15 18 Пример 4. Смесь неявного списка и простых значений. integer a(10), i a = (/ 4, 7, (2*i, i = 1, 8) /) write(*, '(10i3)') a ! 4 7 2 4 6 8 10 12 14 16 Помимо использования в конструкторе массива функции RESHAPE, присваивание значений многомерному массиву можно также выполнить, последовательно применив несколько конструкторов массива, каждый раз определяя линейное сечение массива, например: 120 4. Массивы integer b(2, 3), i, j b(1, :) = (/(i, i = 2, 6, 2)/) ! Присвоим значения первому ряду b(2, :) = (/5, 81, 17/) ! Присвоим значения второму ряду write(*, '(3i3)') ((b(i, j), j = 1, 3), i = 1, 2) Результат: 2 4 6 5 81 17 Помимо присваивания, массив можно изменить при выполнении операторов В/В (в массив можно вывести данные, поскольку он является внутренним файлом (разд. 10.3), а также при использовании массива в качестве фактического параметра процедуры. 4.7. Маскирование присваивания 4.7.1. Оператор и конструкция WHERE В Фортране можно, используя оператор или конструкцию WHERE, выполнить присваивание только тем элементам массива, значения которых удовлетворяют некоторым условиям. Например: integer :: b(5) = (/ 1, -1, 1, -1, 1 /) where(b > 0) b = 2 * b print *, b ! 2 -1 2 -1 2 В Фортране 77 для подобных действий используется цикл do k = 1, 5 if(b(k) .gt. 0) b(k) = 2 * b(k) end do Синтаксис оператора WHERE: WHERE(логическое выражение - массив) присваивание массива Синтаксис конструкции WHERE: WHERE(логическое выражение - массив) операторы присваивания массивов END WHERE WHERE(логическое выражение - массив) операторы присваивания массивов ELSEWHERE операторы присваивания массивов END WHERE Первоначально вычисляется значение логического выражения - массива. Его результатом является логический массив, называемый массивом- маской, под управлением которого и выполняется выборочное присваивание массивов. Такое выборочное присваивание называется маскированием присваивания. Поскольку массив-маска формируется до 121 О. В. Бартеньев. Современный ФОРТРАН выполнения присваивания массивов, то никакие выполняемые в теле WHERE изменения над массивами, входящими в логическое выражение - массив, не передаются в массив-маску. Присутствующие в WHERE массивы должны иметь одинаковую форму. Попытка выполнить в теле оператора или конструкции WHERE присваивание скаляра или массивов разной формы приведет к ошибке компиляции. Значения присваиваются тем следующим после WHERE элементам массивов, для которых соответствующий им элемент массива маски равен .TRUE. Если значение элемента массива-маски равно .FALSE. и если в конструкции WHERE присутствует ELSEWHERE, то будет выполнено присваивание следующих после ELSEWHERE элементов массивов, соответствующих по порядку следования элементу массива-маски. Пример: integer :: a(10) = (/1, -1, 1, -1, 1, -1, 1, -1, 1, -1/) integer :: b(-2:7) = 0 where(a > b) ! Массивы a и b одной формы b = b + 2 elsewhere b = b - 3 a = a + b end where print '(10i3)', a ! 1 -4 1 -4 1 -4 1 -4 1 -4 print '(10i3)', b ! 2 -3 2 -3 2 -3 2 -3 2 -3 end Присутствующие в теле WHERE элементные функции вычисляются под управлением массива-маски, т. е. в момент выполнения присваивания. Например, real :: a(5) = (/1.0, -1.0, 1.0, -1.0, 1/) where(a > 0) a = log(a) не приведет к ошибке, поскольку встроенная элементная функция вычисления натурального логарифма будет вызываться только для положительных элементов массива. Следующий фрагмент также синтаксически правилен: real :: a(5) = (/1.0, -1.0, 1.0, -1.0, 1.0/) where(a > 0) a = a / sum(log(a)) но приведет к ошибке выполнения, поскольку маскирование не распространяется на неэлементные функции и функция SUM вычисления суммы элементов массива будет выполнена до выполнения оператора WHERE. Иными словами, приведенный фрагмент аналогичен следующему: real :: a(5) = (/1.0, -1.0, 1.0, -1.0, 1.0/), s integer k 122 4. Массивы s = sum(log(a)) ! При вычислении суммы возникнет ошибка из-за do k = 1, 5 ! попытки найти логарифм отрицательного числа if(a(k) > 0) a(k) = a(k) / s end do Нельзя передавать управление в тело конструкции WHERE, например, посредством оператора GOTO. Фортран 95 расширил возможности конструкции WHERE. Теперь она может включать оператор ELSEWHERE(логическое выражение - массив). Пример. В векторе a к положительным элементам прибавить число 2, к отрицательным - число 3, а к равным нулю - число 4. integer :: a(9) = (/1, 2, 3, -1, -2, -3, 0, 0, 0/) where(a > 0) a = a + 2 elsewhere(a < 0) ! Эта возможность добавлена стандартом 1995 г. a = a + 3 elsewhere a = a + 4 end where print '(10i3)', a ! 3 4 5 2 1 0 4 4 4 end Кроме того, в CVF конструкция WHERE может иметь имя, употребляемое по тем же правилам, что и имя в конструкции DO или IF. Эта возможность CVF является расширением над стандартом. 4.7.2. Оператор и конструкция FORALL Оператор и конструкция FORALL, наряду с сечениями массивов и оператором и конструкцией WHERE, используются для выборочного присваивания массивов. FORALL может заменить любое присваивание сечений или WHERE. Но возможности FORALL шире: оператором и особенно конструкцией FORALL можно выполнять присваивания несогласованных массивов, т. е. массивов разной формы. Подобно WHERE и сечениям FORALL заменяет циклы с присваиванием массивов, например вместо цикла do i = 1, 100 d(i, i) = 2 * g(i) end do лучше использовать forall(i = 1:100) d(i, i) = 2 * g(i) Синтаксис оператора: FORALL(спецификация триплета & [, спецификация триплета] ... & [, выражение-маска]) оператор присваивания 123 О. В. Бартеньев. Современный ФОРТРАН Синтаксис конструкции: [имя:] FORALL(спецификация триплета & [, спецификация триплета] ... & [, выражение-маска]) операторы конструкции FORALL END FORALL [имя] спецификация триплета имеет вид: индекс = триплет где триплет - это тройка: [нижняя граница]:[верхняя граница]:[шаг]. Каждый из параметров триплета является целочисленным выражением. Параметр шаг изменения индексов может быть и положительным и отрицательным, но не может быть равным нулю; шаг, если он отсутствует, принимается равным единице. Все параметры триплета являются необязательными. В выражениях, задающих нижнюю, верхнюю границы триплета и его шаг, не должно быть ссылок на индекс. Оценка какого-либо выражения триплета не должна влиять на результат его иного выражения. индекс - это скаляр целого типа. Область видимости индекса - оператор или конструкция FORALL. После завершения FORALL значение индекса не определено. выражение-маска - логическое выражение - массив; при отсутствии принимается равным .TRUE.. Содержит, как правило, имена индексов, например: forall(i = 1:n, i = 1:n, a(i, j) /= 0.0) b(i, j) = 1.0 / a(i, j) Переменная, которой в операторе присваивания присваиваются значения, должна быть элементом массива или его сечением и должна содержать имена всех индексов, включенных в спецификации триплетов. Правая часть оператора присваивания не может быть символьного типа. имя - имя конструкции FORALL. операторы конструкции FORALL - это: • оператор присваивания, обладающий рассмотренными выше свойствами; • оператор или конструкция WHERE; • оператор или конструкция FORALL. Присутствующие в FORALL операторы выполняются для тех значений индексов, задаваемых индексными триплетами, при которых выражение- маска вычисляется со значением .TRUE.. В DO-цикле операторы выполняются немедленно при каждой итерации. FORALL работает иначе: первоначально вычисляется правая часть выражения для всех итераций и лишь затем выполняется присваивание. То же справедливо и для выражений с сечениями, например: integer(4), parameter :: n = 5 integer(4), dimension(n) :: a = 1 ! Объявляем и инициализируем массив a |