- •Предисловие
- •1. Элементы языка
- •1.1. Свободная форма записи программы
- •1.2. Консоль-проект
- •1.2.1. Создание проекта в CVF
- •1.2.2. Создание проекта в FPS
- •1.2.3. Операции с проектом
- •1.2.4. Файлы с исходным текстом
- •1.3. Операторы
- •1.4. Объекты данных
- •1.5. Имена
- •1.6. Выражения и операции
- •1.7. Присваивание
- •1.8. Простой ввод/вывод
- •1.8.1. Некоторые правила ввода
- •1.8.2. Ввод из текстового файла
- •1.8.3. Вывод на принтер
- •1.9. Рекомендации по изучению Фортрана
- •1.10. Обработка программы
- •2. Элементы программирования
- •2.1. Алгоритм и программа
- •2.2. Базовые структуры алгоритмов
- •2.2.1. Блок операторов и конструкций
- •2.2.2. Ветвление
- •2.2.3. Цикл
- •2.2.3.1. Цикл "с параметром"
- •2.2.3.2. Циклы "пока" и "до"
- •2.2.4. Прерывание цикла. Объединение условий
- •2.3. Программирование "сверху вниз"
- •2.3.1. Использование функций
- •2.3.2. Использование подпрограмм
- •2.3.3. Использование модулей
- •2.4. Этапы проектирования программ
- •2.5. Правила записи исходного кода
- •3. Организация данных
- •3.1. Типы данных
- •3.2. Операторы объявления типов данных
- •3.2.1. Объявление данных целого типа
- •3.2.2. Объявление данных вещественного типа
- •3.2.3. Объявление данных комплексного типа
- •3.2.4. Объявление данных логического типа
- •3.3. Правила умолчания о типах данных
- •3.4. Изменение правил умолчания
- •3.5. Буквальные константы
- •3.5.1. Целые константы
- •3.5.2. Вещественные константы
- •3.5.3. Комплексные константы
- •3.5.4. Логические константы
- •3.5.5. Символьные константы
- •3.6. Задание именованных констант
- •3.7. Задание начальных значений переменных. Оператор DATA
- •3.8. Символьные данные
- •3.8.1. Объявление символьных данных
- •3.8.2. Применение звездочки для задания длины строки
- •3.8.3. Автоматические строки
- •3.8.4. Выделение подстроки
- •3.8.5. Символьные выражения. Операция конкатенации
- •3.8.6. Присваивание символьных данных
- •3.8.7. Символьные переменные как внутренние файлы
- •3.8.8. Встроенные функции обработки символьных данных
- •3.8.9. Выделение слов из строки текста
- •3.9. Производные типы данных
- •3.9.1. Объявление данных производного типа
- •3.9.2. Инициализация и присваивание записей
- •3.9.2.1. Конструктор производного типа
- •3.9.2.2. Присваивание значений компонентам записи
- •3.9.2.3. Задаваемые присваивания записей
- •3.9.3. Выражения производного типа
- •3.9.4. Запись как параметр процедуры
- •3.9.5. Запись как результат функции
- •3.9.6. Пример работы с данными производного типа
- •3.9.7. Структуры и записи
- •3.9.7.1. Объявление и присваивание значений
- •3.9.7.2. Создание объединений
- •3.9.8. Итоговые замечания
- •3.10. Целочисленные указатели
- •3.11. Ссылки и адресаты
- •3.11.1. Объявление ссылок и адресатов
- •3.11.2. Прикрепление ссылки к адресатам
- •3.11.3. Инициализация ссылки. Функция NULL
- •3.11.4. Явное открепление ссылки от адресата
- •3.11.5. Структуры со ссылками на себя
- •3.11.6. Ссылки как параметры процедур
- •3.11.7. Параметры с атрибутом TARGET
- •3.11.8. Ссылки как результат функции
- •4. Массивы
- •4.1. Объявление массива
- •4.2. Массивы нулевого размера
- •4.3. Одновременное объявление объектов разной формы
- •4.4. Элементы массива
- •4.5. Сечение массива
- •4.6. Присваивание массивов
- •4.7. Маскирование присваивания
- •4.7.1. Оператор и конструкция WHERE
- •4.7.2. Оператор и конструкция FORALL
- •4.8. Динамические массивы
- •4.8.1. Атрибуты POINTER и ALLOCATABLE
- •4.8.2. Операторы ALLOCATE и DEALLOCATE
- •4.8.3. Автоматические массивы
- •4.9. Массивы - формальные параметры процедур
- •4.9.1. Массивы заданной формы
- •4.9.2. Массивы, перенимающие форму
- •4.9.3. Массивы, перенимающие размер
- •4.10. Использование массивов
- •4.11. Массив как результат функции
- •4.12. Встроенные функции для массивов
- •4.12.1. Вычисления в массиве
- •4.12.2. Умножение векторов и матриц
- •4.12.3. Справочные функции для массивов
- •4.12.3.1. Статус размещаемого массива
- •4.12.3.2. Граница, форма и размер массива
- •4.12.4. Функции преобразования массивов
- •4.12.4.1. Элементная функция MERGE слияния массивов
- •4.12.4.2. Упаковка и распаковка массивов
- •4.12.4.3. Переформирование массива
- •4.12.4.4. Построение массива из копий исходного массива
- •4.12.4.5. Функции сдвига массива
- •4.12.4.6. Транспонирование матрицы
- •4.13. Ввод/вывод массива под управлением списка
- •4.13.1. Ввод/вывод одномерного массива
- •4.13.2. Ввод/вывод двумерного массива
- •5. Выражения, операции и присваивание
- •5.1. Арифметические выражения
- •5.1.1. Выполнение арифметических операций
- •5.1.2. Целочисленное деление
- •5.1.3. Ранг и типы арифметических операндов
- •5.1.4. Ошибки округления
- •5.2. Выражения отношения и логические выражения
- •5.3. Задаваемые операции
- •5.4. Приоритет выполнения операций
- •5.5. Константные выражения
- •5.6. Описательные выражения
- •5.7. Присваивание
- •6. Встроенные процедуры
- •6.1. Виды встроенных процедур
- •6.2. Обращение с ключевыми словами
- •6.3. Родовые и специфические имена
- •6.4. Возвращаемое функцией значение
- •6.5. Элементные функции преобразования типов данных
- •6.6. Элементные числовые функции
- •6.7. Вычисление максимума и минимума
- •6.8. Математические элементные функции
- •6.8.1. Экспоненциальная, логарифмическая функции и квадратный корень
- •6.8.2. Тригонометрические функции
- •6.9. Функции для массивов
- •6.10. Справочные функции для любых типов
- •6.11. Числовые справочные и преобразовывающие функции
- •6.11.1. Модели данных целого и вещественного типа
- •6.11.2. Числовые справочные функции
- •6.12. Элементные функции получения данных о компонентах представления вещественных чисел
- •6.13. Преобразования для параметра разновидности
- •6.14. Процедуры для работы с битами
- •6.14.1. Справочная функция BIT_SIZE
- •6.14.2. Элементные функции для работы с битами
- •6.14.3. Элементная подпрограмма MVBITS
- •6.14.4. Пример использования битовых функций
- •6.15. Символьные функции
- •6.16. Процедуры для работы с памятью
- •6.17. Проверка состояния "конец файла"
- •6.18. Неэлементные подпрограммы даты и времени
- •6.19. Случайные числа
- •6.20. Встроенная подпрограмма CPU_TIME
- •7. Управляющие операторы и конструкции
- •7.1. Оператор GOTO безусловного перехода
- •7.2. Оператор и конструкции IF
- •7.2.1. Условный логический оператор IF
- •7.2.2. Конструкция IF THEN END IF
- •7.2.3. Конструкция IF THEN ELSE END IF
- •7.2.4. Конструкция IF THEN ELSE IF
- •7.3. Конструкция SELECT CASE
- •7.4. DO-циклы. Операторы EXIT и CYCLE
- •7.5. Возможные замены циклов
- •7.6. Оператор STOP
- •7.7. Оператор PAUSE
- •8. Программные единицы
- •8.1. Общие понятия
- •8.2. Использование программных единиц в проекте
- •8.3. Работа с проектом в среде DS
- •8.4. Главная программа
- •8.5. Внешние процедуры
- •8.6. Внутренние процедуры
- •8.7. Модули
- •8.8. Оператор USE
- •8.9. Атрибуты PUBLIC и PRIVATE
- •8.10. Операторы заголовка процедур
- •8.10.1. Общие характеристики операторов заголовка процедур
- •8.10.2. Результирующая переменная функции
- •8.11. Параметры процедур
- •8.11.1. Соответствие фактических и формальных параметров
- •8.11.2. Вид связи параметра
- •8.11.3. Явные и неявные интерфейсы
- •8.11.4. Ключевые и необязательные параметры
- •8.11.5. Ограничения на фактические параметры
- •8.11.6. Запрещенные побочные эффекты
- •8.12. Перегрузка и родовые интерфейсы
- •8.12.1. Перегрузка процедур
- •8.12.2. Перегрузка операций и присваивания
- •8.12.3. Общий вид оператора INTERFACE
- •8.13. Ассоциирование имен
- •8.14. Область видимости имен
- •8.15. Область видимости меток
- •8.16. Ассоциирование памяти
- •8.16.1. Типы ассоциируемой памяти
- •8.16.2. Оператор COMMON
- •8.16.3. Программная единица BLOCK DATA
- •8.17. Рекурсивные процедуры
- •8.18. Формальные процедуры
- •8.18.1. Атрибут EXTERNAL
- •8.18.2. Атрибут INTRINSIC
- •8.19. Оператор RETURN выхода из процедуры
- •8.20. Оператор ENTRY дополнительного входа в процедуру
- •8.21. Атрибут AUTOMATIC
- •8.22. Атрибут SAVE
- •8.23. Атрибут STATIC
- •8.24. Атрибут VOLATILE
- •8.25. Чистые процедуры
- •8.26. Элементные процедуры
- •8.27. Операторные функции
- •8.28. Строка INCLUDE
- •8.29. Порядок операторов и директив
- •9. Форматный ввод/вывод
- •9.1. Преобразование данных. Оператор FORMAT
- •9.2. Программирование спецификации формата
- •9.3. Выражения в дескрипторах преобразований
- •9.4. Задание формата в операторах ввода/вывода
- •9.5. Списки ввода/вывода
- •9.5.1. Элементы списков ввода/вывода
- •9.5.2. Циклические списки ввода/вывода
- •9.5.3. Пример организации вывода
- •9.6. Согласование списка ввода/вывода и спецификации формата. Коэффициент повторения. Реверсия формата
- •9.7. Дескрипторы данных
- •9.8. Дескрипторы управления
- •9.9. Управляемый списком ввод/вывод
- •9.9.1. Управляемый именованным списком ввод/вывод
- •9.9.1.1. Объявление именованного списка
- •9.9.1.2. NAMELIST-вывод
- •9.9.1.3. NAMELIST-ввод
- •9.9.2. Управляемый неименованным списком ввод/вывод
- •9.9.2.1. Управляемый неименованным списком ввод
- •9.9.2.2. Управляемый неименованным списком вывод
- •10. Файлы Фортрана
- •10.1. Внешние и внутренние файлы
- •10.2. Позиция файла
- •10.3. Устройство ввода/вывода
- •10.4. Внутренние файлы
- •10.5. Внешние файлы
- •10.6. Записи
- •10.6.1. Типы записей
- •10.6.2. Записи фиксированной длины
- •10.6.3. Записи переменной длины
- •10.6.4. Сегментированные записи
- •10.6.5. Потоки
- •10.6.6. CR-потоки
- •10.6.7. LF-потоки
- •10.7. Передача данных с продвижением и без
- •10.8. Позиция файла перед передачей данных
- •10.9. Позиция файла после передачи данных
- •10.10. Двоичные последовательные файлы
- •10.11. Неформатные последовательные файлы
- •10.12. Текстовые последовательные файлы
- •10.13. Файлы, подсоединенные для прямого доступа
- •10.14. Удаление записей из файла с прямым доступом
- •10.15. Выбор типа файла
- •11. Операции над внешними файлами
- •11.1. Оператор BACKSPACE
- •11.2. Оператор REWIND
- •11.3. Оператор ENDFILE
- •11.4. Оператор OPEN
- •11.5. Оператор CLOSE
- •11.6. Оператор READ
- •11.7. Оператор ACCEPT
- •11.8. Оператор FIND
- •11.9. Оператор DELETE
- •11.10. Оператор UNLOCK
- •11.11. Оператор WRITE
- •11.12. Оператор PRINT
- •11.13. Оператор REWRITE
- •11.14. Оператор INQUIRE
- •11.15. Функция EOF
- •11.16. Организация быстрого ввода/вывода
- •12.1. Некоторые сведения об объектах ActiveX
- •12.2. Для чего нужен конструктор модулей
- •12.3. Интерфейсы процедур управления Автоматизацией
- •12.4. Идентификация объекта
- •12.5. Примеры работы с данными Автоматизации
- •12.5.1. OLE-массивы
- •12.5.2. BSTR-строки
- •12.5.3. Варианты
- •12.6. Другие источники информации
- •12.7. Как воспользоваться объектом ActiveX
- •12.8. Применение конструктора модулей
- •12.9. Пример вызова процедур, сгенерированных конструктором модулей
- •Приложение 1. Вывод русского текста в DOS-окно
- •Приложение 2. Нерекомендуемые, устаревшие и исключенные свойства Фортрана
- •П.-2.1. Нерекомендуемые свойства Фортрана
- •П.-2.1.1. Фиксированная форма записи исходного кода
- •П.-2.1.2. Оператор EQUIVALENCE
- •П.-2.1.3. Оператор ENTRY
- •П.-2.1.4. Вычисляемый GOTO
- •П.-2.1.5. Положение оператора DATA
- •П.-2.2. Устаревшие свойства Фортрана, определенные стандартом 1990 г.
- •П.-2.2.1. Арифметический IF
- •П.-2.2.2. Оператор ASSIGN присваивания меток
- •П.-2.2.3. Назначаемый GOTO
- •П.-2.2.4. Варианты DO-цикла
- •П.-2.2.5. Переход на END IF
- •П.-2.2.6. Альтернативный возврат
- •П.-2.2.7. Дескриптор формата H
- •П.-2.3. Устаревшие свойства Фортрана, определенные стандартом 1995 г.
- •П.-2.4. Исключенные свойства Фортрана
- •Приложение 3. Дополнительные процедуры
- •П.-3.1. Запуск программ
- •П.-3.2. Управление программой
- •П.-3.3. Работа с системой, дисками и директориями
- •П.-3.4. Управление файлами
- •П.-3.5. Генерация случайных чисел
- •П.-3.6. Управление датой и временем
- •П.-3.7. Ввод с клавиатуры и генерация звука
- •П.-3.8. Обработка ошибок
- •П.-3.9. Аргументы в командной строке
- •П.-3.10. Сортировка и поиск в массиве
- •П.-3.11. Управление операциями с плавающей точкой
- •Литература
- •Предметный указатель
- •Оглавление
4. Массивы
заполняет необязательными краевыми значениями образуемые в результате сдвига пропуски.
array - массив любого типа.
shift, dim - имеют тот же смысл, что и для функции CSHIFT.
boundary - необязательный параметр того же типа, что и array. Задает значения, которыми заполняются возникающие в результате сдвига пропуски. Может быть массивом граничных значений, ранг которого на единицу меньше ранга array. Если boundary опущен, то задаваемые по умолчанию замены зависят от типа array: целый - 0; вещественный - 0.0; комплексный - (0.0, 0.0); логический - .FALSE.; символьный - пробел.
Функция возвращает массив, в котором выполнены сдвиг и замены.
Пример:
integer shift(3)
character(1) array(3, 3), ar1(3, 3)
array = reshape((/'a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i'/), (/3, 3/))
! Массив array: |
a |
b |
c |
! |
d |
e |
f |
! |
g |
h |
i |
shift = (/-1, 1, 0/)
ar1 = eoshift (array, shift, boundary = (/'*', '?', '#'/), dim = 2)
! Результат: |
* |
a |
b |
! |
e |
f |
? |
! |
g |
h |
i |
4.12.4.6. Транспонирование матрицы
TRANSPOSE(matrix) - меняет местами (транспонирует) столбцы и строки матрицы (двумерного массива) matrix. Тип и разновидность типа результирующего массива и matrix одинаковы. Если matrix имеет форму (k, n), то результат имеет форму (n, k).
Пример:
integer array(2, 3), result(3, 2)
array = reshape((/1, 2, 3, 4, 5, 6/), (/2, 3/))
! Массив array: |
1 |
3 |
5 |
! |
2 |
4 |
6 |
result = transpose(array) |
|
|
|
! Результат: |
1 |
2 |
|
! |
3 |
4 |
|
! |
5 |
6 |
|
4.13. Ввод/вывод массива под управлением списка
Управляемый списком В/В используется при работе с последовательными текстовыми файлами и стандартными устройствами
153
О. В. Бартеньев. Современный ФОРТРАН
(клавиатура, экран, принтер). Преобразования В/В выполняются в соответствии с типами и значениями вводимых и выводимых величин.
При вводе массива из файла возможны случаи:
•известно число вводимых данных;
•необходимо ввести весь файл, но его размер до ввода неизвестен.
В последнем случае правильнее выполнять ввод в динамический массив.
4.13.1. Ввод/вывод одномерного массива
Рассмотрим пример В/В одномерного статического массива.
integer, parameter :: nmax = 20 integer :: ios, n = 15, i character(60) :: fn = 'a.txt'
real :: a(nmax) = 0
open(2, file = fn, status = 'old', iostat = ios)
if(ios /= 0) then ! Останов в случае ошибки print *, 'Не могу открыть файл ' // trim(fn)
stop end if
if(n > nmax) stop 'Размер списка ввода больше размера массива' read(2, *, iostat = ios) (a(i), i = 1, n)
if(ios /= 0) then
write(*, *) 'Число введенных данных меньше заявленного.'
n = i - 1 |
! n - число введенных значений |
if(eof(2)) backspace 2 |
! Позиционируем файл перед |
end if |
! записью "конец файла" |
write(2, *, iostat = ios) (a(i), i = 1, n) |
|
close(2) |
! Закрываем файл |
write(*,'(1x, 5f5.2)') a(1:n) |
! Контрольный вывод на экран |
end |
|
Состав файла a.txt (до выполнения оператора WRITE(2, *, ...):
1.0 2.0 3.0
4.0 5.0
Отображаемый на экране результат:
Число введенных данных меньше заявленного. 1.00 2.00 3.00 4.00 5.00
Пояснения:
1. Оператор OPEN открывает устройство В/В с номером 2 и подсоединяет к нему файл a.txt. При удачном подсоединении файл a.txt открыт. Далее в программе для доступа к файлу используется номер устройства.
Параметр status = 'old' означает, что открываемый файл должен существовать. Параметр ios позволяет передать в программу код завершения выполнения оператора OPEN. Целочисленная переменная ios
154
4. Массивы
равна нулю при успешном открытии файла и отлична от нуля, если возникла ошибка.
После подсоединения файл позиционируется в свое начало. Файл a.txt открывается и для чтения и для записи. Доступ к файлу последовательный.
2.Контроль ввода выполняется параметром ios: если нет ошибок ввода, то значение ios равно нулю; если достигнут конец файла, значение ios равно -1; ios больше нуля, если имели место иные ошибки. В нашем примере будет достигнут конец файла, однако параметр i циклического списка оператора READ сохраняет свое значение после завершения работы READ, что позволит подсчитать число введенных элементов. Правда, такой способ определения числа введенных элементов не сработает, если ввод данных будет прекращен при обнаружении в файле a.txt слеша (/), поскольку в этом случае ios = 0.
3.Оператор ввода содержит циклический список (a(i), i = 1, n). Это позволяет прочитать первые n произвольно размещенных полей (если, конечно, не возникло ошибки ввода). Ввод выполняется с начала записи, и если оператор ввода должен прочитать больше полей, чем находится в текущей записи, то недостающие поля будут взяты из последующих записей. Каждый оператор READ (если не задан ввод без продвижения) начинает ввод с начала новой записи файла. Поэтому в случае применения цикла
do i = 1, n
read(2, *, iostat = ios) a(i) end do
нам потребовалось бы расположить в текстовом файле каждое число на отдельной строке, т. е. в столбик, что выглядит весьма неуклюже.
С появлением сечений циклический список (a(i), i = 1, n) можно заменить сечением a(1:n). В нашем примере сечение применено в операторе
WRITE.
Для ввода также можно применить оператор: read(2, *, iostat = ios) a ! или a(1:nmax)
Он попытается ввести весь массив - передать из файла первые nmax полей. Однако если полей ввода меньше nmax, то при таком вводе уже нельзя вычислить число введенных данных.
Вывод всего массива выполнит оператор
write(*, *) a |
! Вывод на экран |
4. Вывод массива в файл a.txt приведет к тому, что в файл начиная с новой строки (записи) будут переданы n элементов массива a. Строка, начиная с которой будут передаваться данные, определяется по правилу: если файл установлен на строке line, то новые данные будут добавляться начиная со строки line + 1. Причем, поскольку доступ к файлу
155
О. В. Бартеньев. Современный ФОРТРАН
последовательный, все существующие после строки line данные будут "затерты" (заменены на вновь выводимые). В общем случае при управляемом списком выводе каждый оператор вывода создает одну запись, если длина создаваемой записи не превышает 79 символов. Число полей в созданной записи равно числу элементов в списке вывода. Если же для размещения элементов вывода требуется большее число символов, то создаются новые записи. В качестве разделителей между полями оператор WRITE использует пробелы.
5. Оператор CLOSE(2) закрывает файл a.txt - отсоединяет файл от устройства 2.
Рассмотрим теперь, как ввести весь файл или все его первые числовые поля в размещаемый массив. Прежде следует подсчитать, сколько данных можно ввести из файла, затем выделить под массив память, "перемотать" файл в его начало и ввести в массив данные. Например:
integer, parameter :: nmax = 10000 integer, parameter :: unt = 3 integer :: ios, i, n
character(60) :: fn='a.txt' real tmp
real, allocatable :: a(:)
open(unt, file = fn, status = 'old') read(unt, *, iostat = ios) (tmp, i = 1, nmax)
if(ios == 0) stop 'Нельзя ввести весь файл'
n = i - 1 |
! n - число вводимых данных |
allocate( a(n) ) |
! Выделяем память под массив a |
rewind unt |
! Переход в начало файла |
read(unt, *) a |
! Ввод массива a |
close(unt) |
! Закрываем файл |
write(*, '(1x, 5f5.2)') a(:n) |
|
deallocate(a) |
|
end |
|
4.13.2. Ввод/вывод двумерного массива
Пусть надо ввести из файла b.txt данные в двумерный массив a(1:3, 1:4). Занесем в файл b.txt данные так, чтобы строка файла соответствовала одной строке массива a(1:3, 1:4):
11 12 13 14
21 22 23 24
31 32 33 34
Тогда ввод массива по строчкам можно выполнить в цикле
do i = 1, 3
read(2, *, iostat = ios) (a(i, j), j = 1, 4) ! или a(i, 1:4) end do
156
4. Массивы
Для ввода i-й строки массива вновь использован циклический список. В принципе при управляемом списком вводе двумерного массива можно
использовать и вложенный циклический список: read(2, *, iostat = ios) ((a(i, j), j = 1, 4), i = 1, 3)
Такой оператор также обеспечит ввод данных в массив a построчно (быстрее изменяется параметр j). Однако при этом вводимые данные не обязательно располагать в трех строчках, по 4 числа в каждой. Они могут быть размещены, например, так:
11 12 13 14 21 22 23 24
31 32 33 34
Если же расположение данных в файле соответствует их размещению в столбцах массива (т. е. их порядок в файле совпадает с порядком их размещения в памяти ЭВМ), например так:
11 21 31
12 22 32
13 23 33
14 24 34
или так:
11 21 31 12 22 32 13 23 33 14 24 34
то ввод всего массива можно выполнить оператором read(2, *, iostat = ios) a
Контрольный вывод массива на экран по строкам организуется в цикле
do i = 1, 3
write(*, *) (a(i, j), j = 1, 4) ! или a(i, 1:4) end do
Замечание. В рассмотренных нами примерах к файлам был организован последовательный метод доступа, при котором возможно лишь чтение и запись данных. Редактирование отдельных записей файла становится возможным при прямом методе доступа (гл. 10).
157