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

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

124

4. Массивы

integer(4) :: k

 

 

 

 

 

 

do k = 2, n

! Выполним присваивание в цикле

a(k) = a(k - 1) + 2

 

 

 

 

 

 

end do

 

 

 

 

 

 

print *, a

!

1

3

5

7

9

a = 1

! Присваивание в FORALL

forall(k = 2:n) a(k) = a(k - 1) + 2

 

 

 

 

 

 

print *, a

!

1

3

3

3

3

a = 1

! Используем выражение с сечениями

a(2:n) = a(1:n-1) + 2

 

 

 

 

 

 

print *, a

!

1

3

3

3

3

Ни один из элементов массива не может быть изменен в FORALL более одного раза.

Любая процедура, вызываемая в выражении-маске FORALL, должна быть чистой.

Любую конструкцию или оператор WHERE можно заменить FORALL, обратное утверждение несправедливо. Примером служит оператор

forall(i = 1:n, j = 1:n) h(i, j) = 1.0 / real(i + j)

в котором элементами выражения являются изменяемые индексы, что для WHERE недопустимо.

Пример 1:

type monarch integer(4), pointer :: p end type monarch

type(monarch), dimension(8) :: pattern integer(4), dimension(8), target :: object

forall(j = 1:8) pattern(j)%p => object(1 + ieor(j - 1, 2))

Этот оператор FORALL прикрепляет элементы с номерами 1-8 ссылки pattern соответственно к элементам 3, 4, 1, 2, 7, 8, 5 и 6 адресата object. Встроенная функция IEOR может быть использована, так как она, как и все встроенные процедуры, является чистой.

Пример 2. Использование именованной конструкции FORALL.

ex2: forall(i = 3:n + 1, j = 3:n + 1)

c(i, j) = c(i, j + 2) + c(i, j - 2) + c(i + 2, j) + c(i - 2, j)

d(i, j) = c(i, j)

! Массиву d присваиваются вычисленные

end forall ex2

! в предыдущем операторе элементы массива c

Пример 3. Операторы FORALL, которые не заменяются сечениями или WHERE.

real(4), dimension(100, 100) :: a = 1.0, b = 2.0 real(4), dimension(300) :: c = 3.0

integer(4) :: i, j

forall(i = 1:100, j = 1:100) a(i, j) = (i + j) * b(i, j) forall(i = 1:100) a(i, i) = c(i)

125