Файл: А. Б. Шнейвайс основы программирования.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.12.2023

Просмотров: 347

Скачиваний: 6

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

6.5
Выборочное присваивание (присваивание по маске)
Пусть требуется в сто раз увеличить все положительные элементы мас- сива. ФОРТРАН-77 предоставлял только одну возможность:
program where0; implicit none integer a(10) /-1,0,3,-5,9,-3,8,4,-9,0/
integer i write(*,’(10i4)’) a
!
Результат работы do i=1,10
!
-1 0
3
-5 9
-3 8
4
-9 0
if (a(i).gt.0) a(i)=100*a(i)
!
-1 0 300
-5 900
-3 800 400
-9 0
enddo write(*,’(10i4)’) a end
В современном ФОРТРАНе имеются и альтернативные более удобные возможности. Это — оператор where и конструкция where. Конструк- ция отличается от оператора своей рамочной структурой, что позво- ляет в качестве её выполнимого тела содержать несколько операторов
(т.е., как иногда говорят, блоки операторов и конструкций), в то время как выполнимым телом оператора where может быть лишь один един- ственный оператор. Например,
program where1; implicit none integer a(10) /-1,0,3,-5,9,-3,8,4,-9,0/
integer i
! Здесь оператор where write(*,’(10i4)’) a;
! увеличивает содержимое where (a>0) a=100*a;
! положительных элементов write(*,’(10i4)’) a
! массива a в 100 раз.
where (a>0)
a=a/2
! Здесь конструкция where endwhere
! уменьшает их вдвое.
write(*,’(10i4)’) a where (a>0)
! Здесь конструкция where a=a*2
! не только увеличивает elsewhere
! вдвое положительные, но a=a-1
! и на единицу уменьшает endwhere
! отрицательные.
write(*,’(10i4)’) a end
Результат программы where1
-1 0
3
-5 9
-3 8
4
-9 0
-1 0 300
-5 900
-3 800 400
-9 0
-1 0 150
-5 450
-3 400 200
-9 0
-2
-1 300
-6 900
-4 800 400 -10
-1 224

Оператор where — присваивание по маске имеет вид ( [2])
where (логическое_выражение) оператор_присваивания
• логическое_выражение — выражение логического типа (маска).
• оператор_присваивания — оператор присваивания, у которого левая часть есть массив.
Конструкция where (присваивание по маске) может иметь вид:
where (логическое_выражение)
блок_операторов
[ elsewhere блок_операторов ]
endwhere
Здесь
1. Квадратные скобки — не синтаксический элемент, а лишь напоми- нание, что их содержимое, когда это удобно, можно не писать.
2. Блок_операторов — ряд операторов присваивания, у которых ле- вые части являются массивами.
3. Форма массива-маски должна совпадать с формой массивов в ле- вой части операторов присваивания.
4. Присваивание по маске начинается с вычисления логического выражения маски.
5. Выражение, входящее в правую часть where (или в тело конструк- ции), вычисляется лишь для тех элементов, у которых значение мас- ки — ИСТИНА, и результат присваивается элементам массива, со- ответствующим условию истинности логического выражения.
6. Встречающаяся в правых частях функция может быть поэлемент- ной и непоэлементной.
Для поэлементной функции (НЕ ЯВЛЯЮЩЕЙСЯ фактическим аргументом непоэлементной), как и ожидается, будут вычислять- ся лишь элементы, соответствующие условию истинности маски;
Для непоэлементной — будут вычислены значения всех элементов массива независимо от условия истинности маски. Например,
225

program where2; implicit none; real a(10) /-1,0,3,-5,9,-3,8,4,-9,0/
integer i write(*,’(10f7.2)’) a;
where (a>0)
a=sqrt(a)
write(*,’(10f7.2)’) a where (a>0)
a=sqrt(a)
endwhere write(*,’(10f7.2)’) a end
Результат программы where2
-1.00 0.00 3.00
-5.00 9.00
-3.00 8.00 4.00
-9.00 0.00
-1.00 0.00 1.73
-5.00 3.00
-3.00 2.83 2.00
-9.00 0.00
-1.00 0.00 1.32
-5.00 1.73
-3.00 1.68 1.41
-9.00 0.00
program where3; implicit none
! Здесь ПОЭЛЕМЕНТНАЯ функция sqrt real a(10) /-1,0,3,-5,9,-3,8,4,-9,0/
! является фактическим аргументом write(*,’(10f7.2)’) a;
! НЕПОЭЛЕМЕНТНОЙ функции SUM, на where (a>0)
a=a+sum(sqrt(a))
! которую действие маски write(*,’(10f7.2)’) a
! не распространяется:
where (a>0)
! Поэтому сначала вычислится сумма a=a+sum(sqrt(a))
! корней квадратных от всех endwhere
! элементов массива a, которая write(*,’(10f7.2)’) a
! ввиду отрицательности некоторых end
! из них окажется равной NaN.
Результат программы where3
-1.00 0.00 3.00
-5.00 9.00
-3.00 8.00 4.00
-9.00 0.00
-1.00 0.00
NaN
-5.00
NaN
-3.00
NaN
NaN
-9.00 0.00
-1.00 0.00
NaN
-5.00
NaN
-3.00
NaN
NaN
-9.00 0.00
окажется эквивалентен результату программы:
program where3
implicit none real a(10) /-1,0,3,-5,9,-3,8,4,-9,0/, s integer i s=sum(sqrt(a))
write(*,*) ’ s=’, s write(*,’(10f7.2)’) a;
where (a>0)
a=a+s write(*,’(10f7.2)’) a where (a>0)
a=a+s endwhere write(*,’(10f7.2)’) a end
226

6.6
Оператор и конструкция forall
Полезное средство ФОРТРАНа-95 (не было в ФОРТРАНом-90) [2], наце- ленное на множественное выборочное присваивание значений элементам массива. Пример program forall0; implicit none; integer, parameter :: n=9
integer a(n), b(n,n)
integer i a=(/ (100*i,i=1,n) /)
! Массив A заполняется посредством конструктора b=0
forall (i=1:n) b(i,i)=a(i) ! Оператор forall заполняет диагональ матрицы B
write(*,’(9i5)’) b end program forall0
Результат работы forall0:
100 0
0 0
0 0
0 0
0
WHERE ориентирован на
0 200 0
0 0
0 0
0 0
весь массив целиком.
0 0
300 0
0 0
0 0
0
FORALL позволяет
0 0
0 400 0
0 0
0 0
специфицировать более
0 0
0 0
500 0
0 0
0
широкий класс секций
0 0
0 0
0 600 0
0 0
массива.
0 0
0 0
0 0
700 0
0
Здесь посредством forall
0 0
0 0
0 0
0 800 0
заполнена ДИАГОНАЛЬ
0 0
0 0
0 0
0 0
900
матрицы.
1. Оператор forall:
forall (заголовок) оператор_присваивания
2. Конструкция forall:
[имя] forall (заголовок)
тело конструкции end forall [имя]
Заголовок имеет вид:
триплет [,триплет] ... [, логическое выражение]
Логическое выражение — скалярное логическое выражение типа
(маска).
Триплет:
имя\_индекса = нижний\_индекс:верхний\_индекст [: шаг\_по\_индексу]
Индексы и шаг(может быть и отрицательным) — скалярные целого типа.
Имя индекса может присутствовать в маске.
227

Cхема выполнения forall:
1   ...   7   8   9   10   11   12   13   14   ...   23

1) по триплетам определяется набор допустимых комбинаций индексов.
2) для этого набора вычисляются значения маски;
3) для него же вычисляются выражения в правых частях операторов присваива- ния и в индексах левых частей , если значения маски есть „ИСТИНА”.
4) осуществляется присваивание вычисленных значений соответствующим эле- ментам левой части;
На первый взгляд forall функционально похож на оператор цикла. Од- нако, последовательность действий у них разная. Обратимся к примеру:
program forall1; implicit none
! В программе описаны две матрицы B и С,
integer, parameter :: n=5
! с одинаковыми ненулевыми элементами по integer i, a(n), b(n,n), c(n,n)
! по главной диагонали, заполняемой из a=(/ (100*i,i=1,n) /); b=0
! вектора A так, как в предыдущей задаче.
forall (i=1:n) b(i,i)=a(i); c=b
!
Здесь матрицы B и C
write(*,*) ’b=c:’; write(*,’(5("
",5i5,/) )’) b
!
одинаковы.
forall (i=2:n) b(i,i)=b(i-1,i-1)
!
FORALL !!!
write(*,’(a,33x,a)’) ’
b:’,’Работа forall c B:’
write(*,’( 5("
",5i5/) )’) b do i=2,n; c(i,i)=c(i-1,i-1); enddo
!
DO
!!!
write(*,’(a,33x,a)’) ’
c:’,’Работа do c C:’;
write(*,’( 5("
",5i5/) )’) c end b=c:
Результат работы forall1:
100 0
0 0
0 0
200 0
0 0
0 0
300 0
0 0
0 0
400 0
0 0
0 0
500
b:
Работа forall c B:
100 0
0 0
0 0
100 0
0 0
Здесь все диагональные элементы
0 0
200 0
0
выбираются и используются ДО ТОГО
0 0
0 300 0
как их модифицированное значение
0 0
0 0
400
СОХРАНЯЕТСЯ в оперативной памяти.
c:
Работа do c C:
100 0
0 0
0 0
100 0
0 0
Итерации do происходят последовательно.
0 0
100 0
0
СОХРАНЕНИЕ очередного диагонального
0 0
0 100 0
элемента происходит сразу как только
0 0
0 0
100
он вычислен
228

Пример работы оператора forall с маской:
program forall2a
! Программа демонстрирует использование implicit none
! оператора forall с маской, обеспечивающей integer, parameter :: n=5
! обнуление элементов матрицы, лежащих выше integer i, j; real A(n,n)
! её главной диагонали.
A=reshape((/ ((10.0*i+j, j=1,n),i=1,n)/),(/n,n/))
write(*,’(" Вывод исходной A по умолчанию:"/5(30x,5f6.0/))’) A
write(*,’(18x," по строкам:"/5(30x,5f6.0/))’) ((a(i,j),j=1,n),i=1,n)
write(*,’(18x," по столбцам:"/5(30x,5f6.0/))’) ((a(i,j),i=1,n),j=1,n)
forall (i=1:n,j=1:n, j>i) A(i,j)=0
!
forall !!!
write(*,’(" Result
A:"/5(5f6.0/))’) A
! Вывод результата write(*,’(18x," по строкам:"/5(30x,5f6.0/))’) ((a(i,j),j=1,n),i=1,n)
end
Вывод исходной A по умолчанию:
11.
12.
13.
14.
15.
21.
22.
23.
24.
25.
31.
32.
33.
34.
35.
41.
42.
43.
44.
45.
51.
52.
53.
54.
55.
по строкам:
11.
21.
31.
41.
51.
12.
22.
32.
42.
52.
13.
23.
33.
43.
53.
14.
24.
34.
44.
54.
15.
25.
35.
45.
55.
по столбцам:
11.
12.
13.
14.
15.
21.
22.
23.
24.
25.
31.
32.
33.
34.
35.
41.
42.
43.
44.
45.
51.
52.
53.
54.
55.
Result
A:
11.
12.
13.
14.
15.
0.
22.
23.
24.
25.
0.
0.
33.
34.
35.
0.
0.
0.
44.
45.
0.
0.
0.
0.
55.
по строкам:
11.
0.
0.
0.
0.
12.
22.
0.
0.
0.
13.
23.
33.
0.
0.
14.
24.
34.
44.
0.
15.
25.
35.
45.
55.
229


Замечание:
• Поместив в список вывода результата лишь имя матрицы, получили расположение нулей не над главной диагональю, а под ней.
• Причина — вывод матрицы по столбцам (т.е. в первой строке вывода оказывается первый столбец матрицы и т.д).
• Для вывода матрицы по строкам необходим соответствующий опе- ратор цикла, что и продемонстрировано.
Пример работы конструкции forall с маской:
program forall2b; implicit none
! Программа использует конструкцию integer, parameter :: n=5
! forall с маской A(i)/=0
integer i; real a(n)
! для того, чтобы элементы вектора A
a=(/ (i-3,i=1,n) /)
! приняли значения равные обратной write(*,’("source a:",5f6.0)’) a
! величине их первоначального значения.
forall (i=1:n, a(i)/=0);
! Обратите внимание, что при a(i)=1/a(i)
! наличии в спецификаторе формата f6.0
end forall
! после точки цифры нуль, дробная write(*,’("result a0:",5f6.0)’) a
! часть числа НЕ ВЫВОДИТСЯ. Так write(*,’("result a1:",5f6.1)’) a
! что можно долго недоумевать:
end
! почему это 1/2.0 = 0 (или 1).
source a:
-2.
-1.
0.
1.
2.
result a0:
0.
-1.
0.
1.
0.
result a1:
-0.5
-1.0 0.0 1.0 0.5
Тело конструкции forall может содержать:
операторы: присваивания, where, forall.
Конструкция forall допускает вложенность (см. [2]).
Конструкция может быть снабжена именем, что бывает полезным при наличии вложенных конструкций, т.к. с необходимостью требует за- вершения каждой конструкции соответствующим ей именем — тем са- мым снижается вероятность её некорректного закрытия.
Вообще говоря, помимо операторов старого ФОРТРАНа современный
ФОРТРАН предоставляет соответствующие конструкции. Каждая кон- струкция может иметь имя. Мы уже активно пользовались конструкция- ми if-then-endif, do-enddo, select case-end selectf, но у нас они были неименованными (подробнее см. [2]).
230

6.7
О чем узнали из шестой главы? (второй семестр)
1. Современный ФОРТРАН для работы с массивами (наряду с воз- можностями ФОРТРАНа-77) предоставляет альтернативные, кото- рые позволяют существенно сократить размер исходных текстов.
2. Инициализировать массив можно через конструктор массива.
3. Конструктор массива — одномерный массив.
4. Секция (или сечение) массива обеспечивает доступ к той или иной группе его элементов.
5. Секция массива — массив. Её индексами могут быть:
1) Скалярный индекс — индекс массива по ФОРТРАНу-77;
2) Индексный триплет – символическое обозначение последова- тельности индексов, задаваемой в общем случае тремя призна- ками:
[нижний_индекс] : [верхний_индекс] [: шаг_по_индексу]
3) Векторный индекс — целочисленный вектор, значения эле- ментов которого есть индексы элементов, входящих в секцию.
6. Встроенная функция reshape возвращает через своё имя массив,
в котором размещаются данные из элементов исходного массива source согласно указанной в массиве shape форме результата. По- рядок размещения определяется содержимым массива order. Если размер source меньше размера reshape, то её неприсвоенные эле- менты можно заполнить содержимым аргумента pad.
7. При вызове reshape её фактические аргументы могут быть пози- ционными или ключевыми (в зависимости от способа записи).
8. Фактический аргумент считается позиционным, если присутству- ет при вызове функции без соответствующего ключевого слова.
Пример вызова функции reshape c позиционными аргументами:
c=reshape(a,(/9,4/),(/-1,-2,-3,(i,i=-90,-80,1)/),(/2,1/))
9. В качестве ключевого слова всегда выступает имя формального аргумента. Так, при описании встроенной функции reshape её фор- мальные аргументы имеют имена:
reshape(source, shape , pad, order)
231


10. Фактический аргумент считается ключевым, если ему при вызове функции ему сопоставлено имя формального аргумента.
11. Аргументы pad и order функции reshape при её вызове могут от- сутствовать (если допустимо), т.к. являются необязательными.
12. Для наделения формального аргумента функции свойством необя- зательности используем служебное слово optional.
13. Секция массива допустима в качестве фактического аргумента.
14. Современный ФОРТРАН предоставляет операторы where и forall и одноимённые им конструкции, реализующие возможность выбо- рочного присваивания значений элементов массива по маске.
15. Конструкция отличается от оператора своей рамочной структурой и позволяет в качестве её выполняемого тела использовать блоки операторов и конструкций.
16. Функции могут быть поэлементными и непоэлементными. По- элементная в качестве фактического аргумента может иметь либо скаляр, либо массив. Результатом её работы в первом случае будет скаляр; во втором — массив, значение каждого элемента которо- го получается применением функции к соответствующему элементу массива-аргумента. Примеры поэлементных функций: sin, cos и др.
17. Современный ФОРТРАН позволяет наделять свойством поэлемент- ности и процедуры, описываемые программистом. Для этого при описании функции достаточно снабдить её префиксом elemental.
Поэлементные функции позволяют экономить время при работе с массивами, если компилятор допускает распараллеливание процес- са вычислений. (подробнее см. [2]).
18. Не путаем термин поэлементная функция с понятием функции,
возвращающей через своё имя массив. При описании последней в её теле необходимо специфицировать массив с именем функции. В
случае, если упомянутая функция — внешняя, то в программе, вы- зывающей её, необходимо явное описание соответствующего интер- фейса (подробнее см. [2]).
232

6.8
Шестое домашнее задание (второй семестр).
1. Разработать функции sxy, sxz, syz, получающие соответственно в качестве результата матрицы k-го слоя параллельного грани трёх- мерного динамического массива a(nx,ny,nz). В качестве тестового примера ввести nx=3, ny=4, nz=5 и a=
1 2 3 4 5 6 7 8 9 10 11 12 10 20 30 40 50 60 70 80 90 100 110 120 100 200 300 400 500 600 700 800 900 1000 1100 1200 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 11000 12000 10000 20000 30000 40000 50000 60000 70000 80000 90000 100000 11000 12000 2. Написать функцию myresh2(ord,shape,b) (по сути — упрощённый аналог встроенной reshape), преобразующую вектор b(s(1)*s(2))
в матрицу из s(1) строк и s(2) столбцов. Заполнение матрицы про- водить согласно значениям элементов аргумента ord(2):
при ord=(/1,2/) вести заполнение по росту номера строки;
при ord=(/2,1/) вести заполнение по росту номера столбца.
В главной программе можно использовать reshape, но исключи- тельно с целью подтверждения правильности работы myresh2.
3. Модифицировать mtresh2 так, чтобы параметр ord можно было не писать при вызове функции, если ord=(/1,2/).
4. Написать функцию myresh3(ord,shape,b) (по сути —упрощённый аналог встроенной reshape), которая выполняет работу reshape по преобразованию вектора b(s(1)*s(2)*s(3) в трёхмерный массив из s(1) строк, s(2) столбцов и s(3) листов. Заполнение матрицы проводить согласно значениям элементов аргумента ord(3):
при ord=(/1,2,3/) заполнение сначала идёт по росту номера стро- ки при фиксированном столбце и листе; затем (при переходе к сле- дующему столбцу) снова по росту номера строки; затем по росту номера листа. Продемонстрировать правильную работу myresh3
для всех наборов ord=(/1,3,2/), (/2,1,3/), (/2,3,1/), (/3,1,2/),
(/3,2,1/). В главной программе можно использовать встроенную reshape, но исключительно с целью подтверждения правильности работы myresh3.
233