современный фортран , Бортеньев. О. В. Бартеньев Современный Фортран
Скачать 2.24 Mb.
|
205 О. В. Бартеньев. Современный ФОРТРАН Следует обратить внимание на то, что вся конструкция завершается одним END IF. Ясно, что такая запись более экономна, чем запись, использующая отдельные конструкции IF THEN ELSE END IF, например: IF(ЛВ1) THEN БО1 ELSE IF(ЛВ2) THEN БО2 ELSE БО3 END IF END IF IF(ЛВ1) THEN БО1 ELSE IF(ЛВ2) THEN БО2 ELSE БО3 END IF Пример. Найти число положительных, отрицательных и нулевых элементов массива. integer :: a(100), np = 0, ne = 0, nz = 0, ca, i <Ввод массива a> do i = 1, 100 ca = a(i) val_3: if(ca > 0) then ! val_3 - имя конструкции np = np + 1 else if(ca < 0) then ne = ne + 1 else val_3 nz = nz + 1 end if val_3 end do Замечание. Подсчет искомых значений можно выполнить, применив встроенную функцию COUNT. Правда, вызвать ее придется два раза: np = count(a > 0) ne = count(a < 0) nz = 100 - np - ne 7.3. Конструкция SELECT CASE [имя:] SELECT CASE(тест-выражение) CASE(СП1) [имя] [БОК1] [CASE(СП2) [имя] [БОК2]] [CASE DEFAULT [имя] [БОКn]] END SELECT [имя] 206 7. Управляющие операторы и конструкции Тест-выражение - целочисленное, символьное типа CHARACTER(1) или логическое скалярное выражение. СП - список констант, тип которых должен соответствовать типу тест- выражения. Конструкция SELECT CASE работает так: вычисляется значение тест- выражения. Если полученное значение находится в списке СП1, то выполняется БОК1; далее управление передается на следующий за END SELECT оператор. Если значение в СП1 не находится, то проверяется, есть ли оно в СП2, и т. д. Если значение тест-выражения не найдено ни в одном списке и присутствует оператор CASE DEFAULT, то выполняется БОКn, а далее выполняется расположенный за END SELECT оператор. Если же значение тест-выражения не найдено ни в одном списке и CASE DE- FAULTотсутствует, то ни один из БОКi не выполняется и управление передается на следующий за END SELECT оператор. Список констант СП может содержать одно значение, или состоять из разделенных запятыми констант, или быть задан как диапазон разделенных двоеточием значений, например 5:10 или 'I':'N'. Левая граница должна быть меньше правой. Если задается диапазон символов, то код первого символа должен быть меньше кода второго. Если опущена левая граница, например :10, то в СП содержатся все значения, меньшие или равные правой границе. И наоборот, если опущена верхняя граница, например 5:, то в СП попадают все значения, большие или равные нижней границе. СП может включать также и смесь отдельных значений и диапазонов. Разделителями между отдельными элементами СП являются запятые, например: case(1, 5, 10:15, 33) Нельзя задать в СП диапазон значений, когда тест-выражение имеет логический тип. Каждое значение, даже если оно задано в диапазоне значений, может появляться только в одном СП. SELECT CASE-конструкции могут быть вложенными. При этом каждая конструкция должна завершаться собственным END SELECT. Нельзя переходить посредством оператора GOTO или в результате альтернативного возврата из подпрограммы внутрь конструкции SELECT CASE или переходить из одной CASE секции в другую. Попытка такого перехода приведет к ошибке компиляции. Имя конструкции, если оно задано, обязательно должны иметь операторы SELECT CASE и END SELECT. Пример. Найти число положительных, отрицательных и нулевых элементов целочисленного массива. integer :: a(100), np = 0, ne = 0, nz = 0, i <Ввод массива a> do i = 1, 100 207 О. В. Бартеньев. Современный ФОРТРАН select case(a(i)) case(1:) np = np + 1 case(:-1) ne = ne + 1 case(0) ! или: CASE DEFAULT nz = nz + 1 endselect end do 7.4. DO-циклы. Операторы EXIT и CYCLE Простейшая конструкция DO [имя:] DO БОК END DO [имя] задает бесконечный цикл. Поэтому БОК должен содержать по крайней мере один оператор, например EXIT [имя], обеспечивающий выход из этого цикла. Имя конструкции, если оно присутствует, должно появляться в операторах DO и END DO. Пример. Найти первый отрицательный элемент массива a(1:100). i = 1 first_n: do ! first_n - имя конструкции DO if(a(i) < 0 .or. i == 100) exit first_n i = i + 1 end do first_n if(a(i) >= 0) stop 'В массиве нет отрицательных элементов' print *, a(i) ! Первый отрицательный элемент массива Рекомендуемая форма DO-цикла с параметром: [имя:] DO dovar = start, stop [, inc] БОК END DO [имя] dovar - целая, вещественная одинарной или двойной точности переменная, называемая переменной цикла или параметром цикла; start, stop - целые, вещественные одинарной или двойной точности скалярные выражения, задающие диапазон изменения dovar ; inc - целое, вещественное одинарной или двойной точности скалярное выражение. Значение inc не может быть равным нулю. Если параметр inc отсутствует, то он принимается равным единице. Число итераций цикла определяется по формуле ni = MAX(INT((stop - start + inc) / inc), 0), 208 7. Управляющие операторы и конструкции где MAX - функция выбора наибольшего значения, а функция INT возвращает значение, равное целой части числа. Если DO-цикл с параметром не содержит операторов выхода из цикла, например EXIT, то БОК выполняется ni раз. После завершения цикла значение переменной цикла dovar равно (случай inc > 0): • dovar_ni + inc, если stop ≥ start и цикл не содержит операторов выхода из цикла, где dovar_ni - значение переменной цикла на последней итерации; • dovar_ni, если stop ≥ start и цикл досрочно прерван, например оператором EXIT или GOTO, где dovar_ni - значение переменной цикла dovar в момент прерывания цикла; • start, если stop < start. Аналогично определяется значение dovar и для случая inc < 0. Порядок выполнения DO-цикла с параметром изложен в разд. 2.2.3.1. Нельзя изменять значение переменной цикла в теле цикла: do k = 1, 10 k = k + 2 ! Ошибка. Попытка изменить значение переменной цикла end do При первом выполнении оператора DO dovar = start, stop, inc вычисляются и запоминаются значения выражений start, stop и inc. Все дальнейшие итерации выполняются с этими значениями. Поэтому если stop, start или inc являются переменными и их значения изменяются в теле цикла, то на работе цикла это не отразится. Замечание. В DO-цикле с вещественным одинарной или двойной точности параметром из-за ошибок округления может быть неправильно подсчитано число итераций, на что обращается внимание в прил. 2. Рекомендуемая форма DO WHILE-цикла: [имя:] DO WHILE(ЛВ) БОК END DO [имя] Если DO WHILE-цикл не содержит операторов прерывания цикла, то БОК выполняется до тех пор, пока истинно скалярное ЛВ. DO-цикл, DO-цикл с параметром и DO WHILE-цикл могут быть прерваны операторами GOTO, EXIT и CYCLE, а также в результате выполнения оператора RETURN, обеспечивающего возврат из процедуры. Оператор EXIT [имя] передает управление из DO-конструкции на первый следующий за конструкцией выполняемый оператор. Если имя опущено, то EXIT 209 О. В. Бартеньев. Современный ФОРТРАН обеспечивает выход из текущего цикла, в противном случае EXIT обеспечивает выход из цикла, имя которого присутствует в операторе EXIT. Оператор CYCLE [имя] передает управление на начало DO-конструкции. При этом операторы, расположенные между CYCLE и оператором END DO конца цикла, не выполняются. Если имя опущено, то CYCLE обеспечивает переход на начало текущего цикла, в противном случае CYCLE обеспечивает переход на начало цикла, имя которого присутствует в операторе CYCLE. Пример. Вычислить сумму элементов массива, значения которых больше пяти, завершая вычисления при обнаружении нулевого элемента. integer a(100), sa, c, i <Ввод массива a> sa = 0 do i = 1, 100 c = a(i) if(c == 0) exit ! Досрочный выход из цикла if(c <= 5) cycle ! Суммирование не выполняется sa = sa + c end do print *, sa Замечание. С позиций структурного программирования приведенные вычисления лучше выполнить, применив объединение условий и отказавшись от операторов EXIT и CYCLE: sa = 0 i = 1 do while(a(i) /= 0 .and. i <= 100) if(a(i) > 5) sa = sa + a(i) i = i + 1 end do DO-конструкции могут быть вложенными. Степень вложения неограниченна. Вложенным DO-конструкциям следует давать имена, что повысит их наглядность и позволит в ряде случаев сократить код. Пример. В трехмерном массиве найти первый отрицательный элемент. integer, parameter :: L = 20, m = 10, n = 5 real, dimension(l, m, n) :: a < ввод массива a > loop1: do i = 1, L loop2: do j = 1, m loop3: do k = 1, n if(a(i, j, k) < 0.0) exit loop1 ! Выход из цикла loop1 end do loop3 210 7. Управляющие операторы и конструкции end do loop2 end do loop1 if(i > L) stop 'В массиве нет отрицательных элементов' print *, a(i, j, k) end При работе с DO- и DO WHILE-циклами необходимо помнить: • переменная DO-цикла с параметром dovar не может быть изменена операторами этого цикла; • не допускается переход внутрь цикла посредством выполнения оператора GOTO или альтернативного возврата из подпрограммы; • не допускается переход на начало DO-конструкции посредством оператора CYCLE, расположенного за пределами этой конструкции (попытка такого перехода может быть предпринята при работе с именованными вложенными DO-конструкциями); • если оператор IF, SELECT CASE, WHERE или FORALL появляется внутри цикла, то соответствующий ему оператор END IF, END SELECT, END WHERE или END FORALL должен быть внутри того же цикла. Замечание. Последние три запрета легче контролировать, если соблюдать при записи программы правило рельефа (разд. 2.5). При записи DO- и DO WHILE-циклов могут быть использованы метки. Проиллюстрируем эти формы записи на примере вычисления суммы отрицательных элементов массива a(1:100). ! Вариант 1; цикл завершается пустым оператором CONTINUE sa = 0 do 21, k = 1, 100 ! После метки можно поставить запятую if(a(k) .lt. 0) sa = sa + a(k) 21 continue ! Вариант 2; вместо CONTINUE используется END DO sa = 0 do 22 k = 1, 100 if(a(k) .lt. 0) sa = sa + a(k) 22 end do ! Вариант 3; цикл завершается исполняемым оператором sa = 0 do 23 k = 1, 100 23 if(a(k) .lt. 0) sa = sa + a(k) ! Использующие цикл DO метка [,] WHILE варианты 4, 5 и 6 имеют те же ! различия, что и варианты 1, 2 и 3 ! Вариант 4 k = 1 sa = 0 211 О. В. Бартеньев. Современный ФОРТРАН do 24, while(k .le. 100) ! После метки можно поставить запятую if(a(k) .lt. 0) sa = sa + a(k) k = k + 1 24 end do ! Конструкцию завершает метка END DO ! Вариант 5 k = 1 sa = 0 do 25 while(k .le. 100) if(a(k) .lt. 0) sa = sa + a(k) k = k + 1 25 continue ! Конструкцию завершает метка CONTINUE ! Вариант 6 k = 1 sa = 0 do 26 while(k .le. 100) if(a(k) .lt. 0) sa = sa + a(k) ! Конструкцию завершает метка исполняемый оператор 26 k = k + 1 Замечания: 1. После метки в предложении DO и DO WHILE может стоять запятая, что проиллюстрировано первым и четвертым циклами. 2. Оператор CONTINUE является пустым, ничего не выполняющим оператором и может появляться где угодно среди исполняемых операторов. 3. В случае вложенных DO- или DO WHILE-циклов два или более DO- или DO WHILE-цикла могут иметь общую завершающую метку. Правда, END DO может завершать только один DO- или DO WHILE-цикл. 7.5. Возможные замены циклов Фортран 90 позволяет в задачах с массивами, где ранее использовались циклы, применять более лаконичные и эффективные средства. К ним относятся: • встроенные функции для работы с массивами; • операторы и конструкции WHERE и FORALL (разд. 4.7); • сечения массивов. Проиллюстрируем сказанное примерами. Пример 1. Найти строку матрицы с максимальной суммой элементов. integer, parameter :: m = 10, n = 20 integer kmax(1) ! Номер искомой строки матрицы real a(m, n) <Ввод матрицы a> kmax = maxloc(sum(a, dim = 2)) ! Функция возвращает массив print *, kmax 212 7. Управляющие операторы и конструкции Пример 2. Заменить в векторе b все отрицательные элементы нулями. integer, parameter :: n = 100 real b(n) <Ввод вектора b> where(b < 0) b = 0 ! Замена отрицательных элементов нулями Пример 3. Поменять местами в матрице a первую и последнюю строки. integer, parameter :: m = 10, n = 20 real, allocatable :: temp(:) real a(m, n) <Ввод матрицы a> allocate(temp(1:n)) ! Выделяем память под temp temp = a(1, 1:n) ! Запомним строку 1 в массиве temp a(1, 1:n) = a(m, 1:n) ! Пишем в строку 1 строку m a(m, 1:n) = temp ! Пишем в строку m из temp deallocate(temp) ! Освобождаем память Пример 4. Сформировать вектор c из отрицательных элементов матрицы a. integer, parameter :: m = 5, n = 4 integer k real, allocatable :: c(:) real a(m, n) <Ввод матрицы a> k = count(a < 0) ! Число отрицательных элементов в a if( k == 0 ) stop 'В матрице a нет отрицательных элементов' allocate( c(k) ) c = pack(a, a < 0) ! Переносим в c отрицательные элементы a Пример 5. Вывести построчно двумерный массив. integer, parameter :: m = 5, n = 3 integer i ! Элементы матрицы размещены в памяти по столбцам integer a(m, n) / 1, 2, 3, 4, 5, & 1, 2, 3, 4, 5, & 1, 2, 3, 4, 5 / do i = 1, m ! Помещаем в список вывода сечение массива print '(100i3)', a(i, 1:n) end do В последнем примере вместо неявного цикла (a(i, j), j = 1, n) в списке вывода присутствует сечение массива, которое так же, как и неявный цикл, задает отдельную строку массива. Аналогичным образом сечение массива можно использовать и при вводе. Однако замены циклов сечениями не всегда возможны. Это прежде всего касается циклов, в которых результат итерации зависит от результата предыдущей итерации. Так, цикл 213 О. В. Бартеньев. Современный ФОРТРАН do i = 2, n a(i) = a(i - 1) + 5.0 end do нельзя заменить присваиванием a(2:n) = a(1:n - 1) + 5.0 или конструкцией forall(i = 2:n) a(i) = a(i - 1) + 5.0 7.6. Оператор STOP Оператор прекращает выполнение программы. Имеет синтаксис: STOP [сообщение] Сообщение - символьная или целочисленная константа в диапазоне от 0 до 99999. Если сообщение отсутствует, то после выполнения оператора выводится строка STOP - Program terminated. Если сообщение является символьной константой, то: • выводится сообщение; • программа возвращает 0 в операционную систему. Если сообщение является числом, то: • выводится предложение Return code число. Например, если применен оператор STOP 0400, после завершения программы будет выведено предложение Return code 0400; • программа возвращает в операционную систему последний значащий байт целого числа (значения от 0 до 255) для использования программой, проверяющей значения статусов выполняемых процессов. Пример: open(2, file = 'b.txt', status = 'old', iostat = icheck) if(icheck .ne. 0) then stop 'File access denied.' end if 7.7. Оператор PAUSE Оператор временно приостанавливает выполнение программы и позволяет пользователю выполнить команды операционной системы. Имеет синтаксис: PAUSE [сообщение] Сообщение - символьная или целочисленная константа в диапазоне от 0 до 99999. Если сообщение отсутствует, то после выполнения оператора выводится строка Please enter a blank line (to continue) or a system command. 214 7. Управляющие операторы и конструкции После выполнения оператора PAUSE возможны следующие действия: • если пользователь вводит пробел, то управление возвращается программе; • если пользователь вводит команду, то выполняется команда и управление возвращается программе. Максимальный размер задаваемой на одной строке команды составляет 128 байт; • если введено слово COMMAND или command, то пользователь может выполнить последовательность команд операционной системы. Для возврата в программу потребуется ввести EXIT (прописными или строчными буквами). Пример: character(30) filename pause 'Enter DIR or press Enter to return.' read(*, '(a)') filename open(1, file = filename) Замечание. Стандартом 1995 г. оператор PAUSE исключен из Фортрана. Однако он по-прежнему поддерживается и CVF и FPS. 215 8. Программные единицы 8.1. Общие понятия При разработке алгоритма исходная задача, как правило, разбивается на отдельные подзадачи. Процесс выделения подзадач крайне важен. Можно без преувеличения сказать, что квалификация программиста во многом определяется его способностью рационально разбить исходную задачу на фрагменты, которые впоследствии реализуются в виде отдельных программных единиц. В Фортране 77 выделенные фрагменты оформлялись, как правило, в виде главной программы и внешних процедур. Выделенные фрагменты должны обмениваться данными. В Фортране 77 такой обмен выполнялся через параметры процедур, common-блоки и, в случае процедуры-функции, через возвращаемое ею значение. В программной единице BLOCK DATA переменным именованного com- mon-блока можно было задать начальные значения. Фортран 90 и 95, сохраняя все возможности Фортрана 77, дополнительно позволяют: • создавать модули, содержащие глобальные данные и модульные процедуры; • создавать внутренние процедуры, расположенные внутри главной программы, внешней или модульной процедуры. Эти нововведения дополнительно позволяют: • использовать различным программным единицам объявленные в модуле глобальные данные и имеющиеся в нем процедуры; • использовать объявленные в модуле данные во всех процедурах этого модуля; • использовать один и тот же объект данных во внутренней процедуре и в ее носителе. Таким образом, в Фортране можно создать такие программные единицы, как: • главная программа; • модули; • внешние процедуры; • внутренние процедуры; • BLOCK DATA. Дополнительно к названным в программной единице (кроме BLOCK DATA) можно определить и операторные функции (разд. 8.24). Модуль - это самостоятельная программная единица, которая может в общем случае содержать объявления данных, common-блоков, интерфейсы процедур, namelist-группы и модульные процедуры. Все не |