Информационные системы ускорителей

ЛАБОРАТОРНЫЙ ПРАКТИКУМ:
«ПРОГРАММИРОВАНИЕ НА ФОРТРАНЕ»

СТРУКТУРА ПРАКТИКУМА

ВВЕДЕНИЕ

ВАЖНАЯ ИНФОРМАЦИЯ

1. ПРАВИЛА ЗАПИСИ ПРОГРАММЫ

1. Набор символов Фортрана

2. Форматы записи программы

3. Фиксированный формат

4. Свободный формат

2. ТРАНСЛЯЦИЯ ПРОГРАММЫ

1. Программа в одном исходном файле

2. Трансляция исходного файла

3. Трансляция нескольких исходных файлов

4. Трансляция модулей

3. КОНЦЕПЦИЯ ДАННЫХ ЯЗЫКА ФОРТРАН

1. Имена (идентификаторы)

2. Понятие типа

3. Буквальные константы

4. Разновидности типов и диапазоны значений

5. Скалярные переменные и константы

6. Массивы

7. Производные типы данных

4. ВЫРАЖЕНИЯ И ПРЕОБРАЗОВАНИЕ ТИПОВ

1. Скалярное присваивание

2. Арифметика Фортрана

3. Логические выражения

4. Работа с текстовыми строками

5. Операции с массивами

5. УПРАВЛЯЮЩИЕ ОПЕРАТОРЫ

1. Условный оператор и конструкция IF

2. Оператор варианта – конструкция CASE

3. Циклы – разновидности конструкции DO

4. Оператор GO TO

6. ВВОД/ВЫВОД ДАННЫХ

1. Простейшие операции ввода/вывода

2. Форматный ввод/вывод данных

3. Ввод/вывод массивов в неявных циклах

4. Файловый ввод/вывод

7. ПРОГРАММНЫЕ КОМПОНЕНТЫ И ЭЛЕМЕНТЫ ООП

1. Структура программных компонентов

2. Внешние подпрограммы

3. Внутренние подпрограммы

4. Модули как библиотеки производных типов

5. Встроенные функции Фортрана

ЗАДАЧИ ДЛЯ ПРОГРАММИРОВАНИЯ

3.6. Массивы

Массивы представляют собой индексированные (нумерованные) набор скалярных объектов (переменных или констант) одного типа. Доступ к элементам массивов осуществляется по индексам (номерам) элементов в массивах.


unix-workdir

Рис. 3.1 Массив ранга три (трехмерный).


Легко представить себе куб (Рис. 3.1), состоящий из мелких кубиков, каждый из которых пронумерован тремя индексами – это модель распределения памяти для трехмерного массива (назовем его CUBE – имя не имеет значения). Элементы массива будут нумероваться от CUBE(1, 1, 1) до CUBE(4, 4, 4).

Ранг массива равен трем (хотя чаще вместо термина ранг употребляются старые термины, как размерность или число измерений). Максимальный ранг массивов в Фортране не может быть больше семи. Размер массива, т.е. общее число элементов в массиве CUBE составляет шестьдесят четыре элемента, а экстенты – или протяженности по каждому измерению одинаковы и равны четырем. Совокупность экстентов массива определяют его форму. Для CUBE ранг массива (равный трем ) и форма: (3, 3, 3) – полностью определяет его как массив в модели данных Фортрана 90/95.

Массивы (переменные и константы) объявляются аналогично скалярным переменным и константам, но с дополнительным атрибутом DIMENSION (который, собственно и указывает на то, что объявляемый объект является массивом).

Массивы подразделяются на статические (их размер – количество элементов – определяется до начала выполнения программы) и динамические (размер которых выясняется в процессе выполнения программы).

Для начала рассмотрим статические массивы. Из них простейшими являются одномерные массивы (c размерностью или рангом, равным единице). Одномерный массив является аналогом такого математического объекта как последовательность. Например, последовательность цифр от 0 до 9, может представлять собой одномерный массив целых констант c именем DIGITS (как в Примере 3.19). То, что описываемый объект является массивом, состоящим из 10 элементов – показывает атрибут DIMENSION, с указанием в круглых скобках размерности массива. Наличие атрибута PARAMETER говорит о том, что элементы массива являются константами.


unix-workdir

Пример 3.19. Объявление и инициализация массива констант с указанием верхней границы.

program ARRINIT1
integer (kind=1), parameter, dimension(10):: DIGITS = (/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9/)
print*, DIGITS
end
	

В приведенном примере одномерный массив DIGITS объявлен как DIMENSION(10). Величина 10 – это размер (количество элементов) в массиве. В то же время такая запись показывает, что 10 – это верхняя граница массива (максимальный номер элемента) а нижняя граница массива (минимальный номер элемента) будет равна единице, поскольку явно не определена. Это означает, что элементы массива DIGITS будут нумероваться следующим образом (и иметь соответствующие значения):

DIGITS (1) = 0, DIGITS (2) = 1, … , DIGITS (10) = 9

В наиболее общем случае задаются обе границы массива – они могут иметь положительное, отрицательное или нулевое (но обязательно целое) значение. При этом важно чтобы значение верхней границы было больше значения нижней границы.

Можно изменить нумерацию элементов массива DIGITS потребовав, например, чтобы они нумеровались с нуля:

DIGITS (0) = 0, DIGITS (1) = 1, … , DIGITS (9) = 9

Для этого нужно явно задать нижнюю границу массива с учетом того, что по правилам Фортрана верхняя и нижняя границы массива в атрибуте DIMENSION разделяются двоеточием (Пример 3.20).


unix-workdir

Пример 3.20. Объявление и инициализация массива констант с указанием нижней и верхней границы.

program ARRINIT2
integer (kind=1), parameter, dimension(0:9):: DIGITS = (/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9/)
print*, DIGITS
end
	

Для инициализации (присвоения значений элементам) одномерных массивов в Фортране 90/95 предусмотрен специализированный конструктор (он использован Примерах 3.19 и 3.20), который в простейшем случае представляет собой список (через запятую) буквальных констант и ограниченный лексемами «(/» и «/)».


unix-workdir

Пример 3.21. Объявление и инициализация массива констант при помощи конструктора.

program ARRINIT3
integer (kind=1), parameter, dimension(0:9):: DIGITS = (/ ( I, I = 0, 9) /)
print*, DIGITS
end
	

Конструктор инициализации массивов (Пример 3.21) позволяет представлять список инициализации в виде значений, зависящих от некоторой переменной: например список: 0, 1, 2, …, 9 может быть представлен в виде неявного цикла: ( I, I = 0, 9). Здесь переменная цикла I (имя не принципиально, но это должна быть целая величина) пробегает все целые значения от 0 до 9.

Конструкторы инициализации массивов позволяют формировать списки значений, подчиненные более сложным арифметическим закономерностям. Например, можно составить список только нечетных элементов от 1 до 9 или в десять раз меньших элементов:

В первом случае значение переменной неявного цикла I начинается с единицы, заканчивается девяткой, при этом для отбора только нечетных значений шаг изменения I равен двум: ( I, I = 1, 9, 2).

Во втором случае шаг изменения I стандартный (равен единице и потому не указывается), но добавляется масштабирующий множитель: 0.1, уменьшающий значения, пробегаемые переменной цикла I в десять раз: ( I*0.1, I = 1, 9, 2).

Следует отметить, что неявные циклы имеют большое значение и часто применяются при выводе массивов на печать, в файлы и т.д. (например, в операторе PRINT – Пример 3.22):


unix-workdir

Пример 3.22. Использование неявных циклов при инициализации массива и его выводе на печать.

program ARRINIT4
integer (kind=1), parameter, dimension(0:9):: DIGITS = (/ ( I, I = 0, 9) /)
print*, (DIGITS(I), I = 0, 9))
end
	

Для приведенных примеров важно еще раз отметить, что список значений элементов (/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9/) и диапазон индексов (0:9) это разные вещи, не смотря на то, что выглядят одинаково

Широкое распространение в компьютерных вычислениях имеют двумерные массивы (c размерностью или рангом, равным двум). Двумерный массив является аналогом такого математического объекта как двумерная таблица или матрица, например:

			1	2	3
			4	5	6
			7	8	9
	

Элементы двумерного массива (например, матрицы – обозначим ее переменной или константой MATRIX) адресуются двумя индексами: номером строки, часто обозначаемым целой переменной I и номером столбца – за него обычно отвечает целая переменная J.

Таким образом, любой элемент матрицы может быть обозначен как MATRIX(I, J), где I и J могут принимать целые значения в диапазоне от единицы до трех. Так, элемент «5» приведенной выше матрицы будет иметь индексацию MATRIX(2, 2).

В машинной памяти все двумерные массивы размерности (M, N) хранятся как одномерные массивы, имеющие сквозную нумерацию от 1 до (M*N) и сформированные из последовательности столбцов соответствующей матрицы

Рассмотрим вопрос об объявлении и инициализации подобного массива MATRIX (Пример 3.23). При этом будем считать, что элементы матрицы переменными целого типа INTEGER стандартной разновидности.

Для инициализации массива воспользуемся оператором DATA, который позволяет присвоить массиву-переменной список значений: разделителями элементов списка значений являются запятые, а сам список целиком заключен в прямые слеши «/»:


Пример 3.23. Объявление и инициализация двумерного массива переменных при помощи оператора DATA и его построчный вывод.

program ARRINIT5
integer, dimension(3, 3) :: MATRIX
data MATRIX /1, 2, 3, 4, 5, 6, 7, 8, 9/
I=1; print*, (MATRIX(I, J), J = 1, 3)
I=2; print*, (MATRIX(I, J), J = 1, 3)
I=3; print*, (MATRIX(I, J), J = 1, 3)
end
	

Если инициализирующий список матрицы MATRIX состоит из чисел от единицы до девяти в порядке возрастания то, при выводе на экран, получается не совсем тот результат, который ожидался. Ожидалась матрица:

			1	2	3
			4	5	6
			7	8	9
	

При этом на экране мы видим, что первая строка стала первым столбцом, вторая – вторым столбцом, а третья – третьим.

			1	4	7
			2	5	8
			3	6	9
	

Это произошло потому, что при поточном вводе данных в массив (например, оператор DATA) они записываются по столбцам, а не по строкам – это одна из особенностей Фортрана (например, в языке Си, при подобном вводе, данные запишутся по строкам). Если внести соответствующие изменения входных данных, то все станет на свои места (Пример 3.24).

Вернемся к вопросу об индексации массивов. При объявлении двумерного массива MATRIX в атрибуте DIMENSION(3, 3) указаны только верхние границы массива по каждому измерению – это означает, что нижние границы по каждому измерению равны единице. Как это было в случае с одномерным массивом или как это в принципе допустимо с семимерным массивом, можно указывать диапазоны границ по каждому измерению (Пример 3.24).


Пример 3.24. Объявление и инициализация двумерного массива переменных при помощи оператора DATA и его построчный вывод.

program ARRINIT6
integer, dimension(3, 3) :: MATRIX
data MATRIX /1, 4, 7, 2, 5, 8, 3, 6, 9/
I=1; print*, (MATRIX(I, J), J = 1, 3)
I=2; print*, (MATRIX(I, J), J = 1, 3)
I=3; print*, (MATRIX(I, J), J = 1, 3)
end
	

Диапазон индексов представляет собой нижнюю и верхнюю границы индекса, разделенные двоеточием (Пример 3.25)


Пример 3.25. Объявление и инициализация двумерного массива с явным указанием диапазона индексов по каждому измерению.

program ARRINIT7
integer, dimension(1:3, 1:3) :: MATRIX
data MATRIX /1, 4, 7, 2, 5, 8, 3, 6, 9/
I=1; print*, (MATRIX(I, J), J = 1, 3)
I=2; print*, (MATRIX(I, J), J = 1, 3)
I=3; print*, (MATRIX(I, J), J = 1, 3)
end
	

При необходимости можно было бы определить матрицу MATRIX, как DIMENSION(–1:1, 0:2) с диапазоном индекса строк – от минус единицы до плюс единицы (с учетом нуля, размерность будет равна трем) и диапазоном столбцов от нуля до двух.

В примерах 3.20 ÷ 3.22 показана инициализация массивов при помощи конструктора инициализации, пригодного только для одномерных массивов. Для того чтобы воспользоваться таким конструктором для инициализации двумерных (и более) массивов, используется функция RESHAPE (Пример 3.26).


unix-workdir

Пример 3.26. Объявление и инициализация двумерного массива с применение конструктора инициализации и функции преобразования формы.

program ARRINIT8
integer, dimension(3, 3) :: MATRIX=RESHAPE( (/1, 4, 7, 2, 5, 8, 3, 6, 9/), (/3,3/) )
I=1; print*, (MATRIX(I, J), J = 1, 3)
I=2; print*, (MATRIX(I, J), J = 1, 3)
I=3; print*, (MATRIX(I, J), J = 1, 3)
end
	

В простейшем случае эта функция содержит два аргумента (в круглых скобках, через запятую): сам конструктор одномерного массива констант и константу, определяющую форму конечного массива – верхние границы индексов, которые указываются для атрибута DIMENSION, но в упаковке лексем «(/» и «/)».

Возможно объявление и инициализация массивов в стиле ФОРТРАН 77 (с сохранением свободного формата записи), с операторами PARAMETER и DIMENSION (Пример 3.27). Это можно проиллюстрировать на примере рассмотренных выше массивов: массива-константы DIGITS и массива-переменной MATRIX.


Пример 3.27. Объявление и инициализация массивов в стиле ФОРТРАН 77, при помощи операторов PARAMETER и DIMENSION.

program ARRINIT9
integer DIGITS	! MATRIX – объект целого типа по умолчанию
dimension DIGITS(10), MATRIX(3,3)
parameter  (DIGITS=(/0, 1, 2, 3, 4, 5, 6, 7, 8, 9/) )
data MATRIX /1, 4, 7, 2, 5, 8, 3, 6, 9/
print*, DIGITS
I=1; print*, (MATRIX(I, J), J = 1, 3)
I=2; print*, (MATRIX(I, J), J = 1, 3)
I=3; print*, (MATRIX(I, J), J = 1, 3)
end
	

В заключение рассмотрим работу с динамическими или, как они называются в Фортране, выделяемыми массивами (Пример 3.28). Для объявления выделяемого массива помимо уже известного атрибута DIMENSION требуется еще атрибут – ALLOCATABLE. Ранг массива описывается в атрибуте DIMENSION как шаблон, т.е. на месте диапазонов границ по каждому измерению просто ставится двоеточие (границы при этом не указываются).


Пример 3.28. Объявление и инициализация динамического массива.

program ARRINIT10
real, dimension(:,:), allocatable :: MATRIX
N=2			! В качестве теста берется матрица 2х2
ALLOCATE( MATRIX(1:N, 1:N) ) ! Можно оставить только верхние границы
read *, MATRIX	!Чтение массива. Ввести четыре числа через пробел
I=1; print*, (MATRIX(I, J), J = 1, 2)
I=2; print*, (MATRIX(I, J), J = 1, 2)
DEALLOCATE( MATRIX)
end
	

Память для такого массива выделяется после успешного выполнения оператора ALLOCATE (до этого момента границы массива по каждому измерению остаются неопределенными) В ALLOCATE границы (в данном случае массива MATRIX) описываются в круглых скобках после имен массивов, так же как для атрибута DIMENSION при работе со статическими массивами.

Если выделяемый массив MATRIX больше не нужен, то можно освободить от него память при помощи оператора DEALLOCATE.