Аверянов Введение в оператсионные системы и основы программирования 2015
.pdfБинарная операция, примененная к двум массивам, имеет такой же результат, как если бы эта операция была попарно применена к элементам первого и второго массива с одинаковыми номерами (индексами). Это легко можно увидеть на примере арифметических операций (см. табл. 3.3) между одномерными (пример 3.61) и двумерными (пример 3.62) массивами вещественных чисел, т.е. операции с массивами не имеют ничего общего с операциями над матрицами или векторами.
Допускаются бинарные операции между массивами и скалярами. Действие бинарной операции между массивом и скаляром равноценно совокупности операций между скаляром и всеми элементами массива, например операция умножения суммарного массива A+B (см. пример 3.62) на скалярную переменную X = 2 равноценна удвоению каждого элемента этого массива. Действие унарных операций (например, унарный минус) имеет аналогичный характер.
Пример 3.62. Объявление и инициализация массива констант с указанием верхней границы (вариант 2)
program OPERARR2
integer, parameter, dimension(2, 2):: A = RESHAPE ((/ 0, 1, 2, 3 /), (/2, 2/) ) integer, parameter, dimension(2, 2):: B = RESHAPE ((/ 4, 5, 6, 7 /), (/2, 2/) ) integer, dimension(2, 2):: C; integer :: X = 2
C = X*(A+B)
I=1; print*, (A(I, J), J = 1, 2); I=2; print*, (A(I, J), J = 1, 2) I=1; print*, (B(I, J), J = 1, 2); I=2; print*, (B(I, J), J = 1, 2) I=1; print*, (C(I, J), J = 1, 2); I=2; print*, (C(I, J), J = 1, 2) end
Для пояснения текста программы (см. пример 3.62) полезно напомнить, что для инициализации массивов A и B используется функция RESHAPE, позволяющая переформатировать одномерные массивы к требуемой форме. В данном случае эта функция содержит два аргумента (в круглых скобках, через запятую): первым аргументом является конструктор одномерного массива констант, а вторым аргументом − константа, определяющая форму конечного массива (см. п. 3.3.6).
171
3.5.УПРАВЛЯЮЩИЕ ОПЕРАТОРЫ
3.5.1.Условный оператор и конструкция IF
Условный оператор IF проверяет значение скалярного логического выражения. Если его значение истинно, т.е. имеет значение «.TRUE.» (см. п. 3.3.3), то выполняется действие в виде одиночного оператора (например, вывод на печать, присваивание и т. д.). Обрамляющие круглые скобки для условия обязательны:
IF (условие) одиночный оператор
Оператору IF не могут в качестве действия указываться другие условные операторы и конструкции, а также циклы. Условием может быть логическая константа, переменная или выражение – чаще всего это логические отношения, связанные логическими операто-
рами (см. п. 3.4.3).
В приведенном примере (пример 3.63) определяется знак введенного с клавиатуры целого числа (положительное или отрицательное), или сообщается, что это нуль. В качестве условий используются простейшие логические отношения (см. табл. 3.6).
Пример 3.63. Пример использования логического оператора IF program SIGNDETECT
integer NUMBER read*, NUMBER
if(NUMBER > 0) print*, “Положительное” if(NUMBER < 0) print*, “Отрицательное” if(NUMBER == 0) print*, “Нуль”
end
Оператор IF позволяет выполнить одиночный оператор (или исключить его выполнение при невыполнении условия) – это в ряде случаев позволяет обеспечить компактность программы, но явно недостаточно, если в качестве альтернатив используются последовательности более чем из одного оператора. В этом случае целесообразно использовать конструкцию IF.
Первый блок конструкции IF всегда начинается со строки, содержащей ключевое слово IF, за которым в круглых скобках следу-
172
ет проверяемое условие (логическая величина) и ключевое слово THEN. После THEN в первой строке конструкции IF не может быть ничего, кроме комментария. Если строка – длинная, она может быть продолжена в соответствии с правилами продолжения строк свободного или фиксированного формата. После первой строки конструкции IF следует последовательность операторов, выполняемых при истинности проверяемого условия:
IF (условие) THEN
блок операторов
ELSE IF (альтернативное условие 1) THEN альтернативный блок операторов 1 ELSE IF (альтернативное условие 2) THEN альтернативный блок операторов 2
…
ELSE IF (альтернативное условие N) THEN альтернативный блок операторов N
ELSE
пост-альтернативный блок
END IF
Далее в конструкции IF могут присутствовать число альтернативных блоков ELSE IF со своими условиями – правила их записи точно такие же, как для первого IF-блока. Количество блоков ELSE IF не ограничено – их может быть и десять и сто, а может не быть ни одного.
Затем после блоков ELSE IF может следовать (или отсутствовать) блок ELSE, содержащий программный код, выполняемый в отсутствие истинности всех условий IF и ELSE IF. После чего для завершения конструкции IF используется оператор END IF.
Таким образом, простейшая форма конструкции IF выгляди следующим образом:
IF (условие) THEN
блок операторов
END IF
173
В качестве примера c одним альтернативным блоком в конструкции IF рассмотрим определение четности целого числа, введенного с клавиатуры.
Для определения четности числа используется правило целочисленной арифметики, при делении целых чисел дробная часть отбрасывается. Если четное целое число сначала разделить на два, а затем результат деления опять умножить на два, получится то же самое четное число, что и до деления. Если ту же операцию проделать с нечетным числом, то при делении на два дробная часть будет отброшена и при умножении результата деления на два исходное нечетное число не восстановится (пример 3.64).
Пример 3.64. Пример логической конструкции IF с альтернативой program EVENDETECT
integer NUMBER logical EVEN read*, NUMBER
EVEN = NUMBER / 2 * 2 == NUMBER if(EVEN) then
print*, “Четное”
else
print*, “Нечетное” end if
end
Развернутую конструкцию IF можно показать на примере, где одновременно определяется знак и четность целого числа, введенного с клавиатуры (пример 3.65).
Пример 3.65. Пример логической конструкции IF с множеством альтернатив
program EVENSIGN integer NUMBER logical EVEN read*, NUMBER
EVEN = NUMBER / 2 * 2 == NUMBER if(EVEN .and. NUMBER > 0) then
print*, “Четное положительное” else if(EVEN .and. NUMBER < 0) then print*, “Четное отрицательное”
else if((.not. EVEN) .and. NUMBER > 0) then print*, “Нечетное положительное”
else if((.not. EVEN) .and. NUMBER < 0) then
174
print*, “Нечетное отрицательное”
else
print*, “Нуль” end if
end
Для конструкций IF допускается произвольная вложенность при условии, что вложенные элементы не пересекаются.
3.5.2. Оператор варианта – конструкция CASE
Подобно конструкции IF конструкция CASE позволяет выполнять тот или иной блок операторов, в зависимости от определенного условия. Условие выбора задается значением скалярного выражения целого, логического или текстового типа. Выражение, определяющее условие выбора, должно быть записано в круглых скобках в операторе SELECT CASE:
SELECT CASE (выражение) CASE селектор 1
блок операторов 1 CASE селектор 1
блок операторов 1
…
CASE селектор N
блок операторов N
CASE DEFAULT
блок операторов
END SELECT
Для выбора блока операторов необходимо, чтобы селектор оператора CASE этого блока содержал элемент, совпадающий по значению с выражением в SELECT CASE. Общая форма селектора – заключенный в скобки список неперекрывающихся значений и интервалов того же типа, что и выражение, определяющее условие выбора блока исполняемых операторов.
175
В качестве примера (пример 3.66) можно запрограммировать алгоритм определения четности целых чисел (тот же алгоритм, что и в примере 3.64).
Селекторы могут задаваться списком, диапазоном или комбинировано (диапазон, как элемент списка). При этом диапазоны значений селекторов не должны пересекаться в пределах одной конст-
рукции SELECT CASE (пример 3.67).
Пример 3.66. Пример логической конструкции IF с альтернативой program EVENCASE
integer NUMBER logical EVEN read*, NUMBER
EVEN = NUMBER / 2 * 2 == NUMBER select case (EVEN)
case (.TRUE.) print*, “Четное”
case (.FALSE.)
print*, “Нечетное” end select
end
Пример 3.67. Задание селекторов в виде списка и закрытого диапазона program DETECTSYM
character SYMBOL read*, SYMBOL select case (SYMBOL)
case(“–” , “+”, “/”, “*”) ! Селектор в виде списка print*,'Арифметический оператор'
case(“0”: “9”) ! Селектор в виде закрытого диапазона значений print*,'Цифра'
case default print*,'Неизвестный символ'
end select end
Селекторы могут задаваться списком и закрытым диапазоном, а в примере 3.68 значения селектора задаются как закрытым, так и открытым диапазоном.
Закрытый диапазон значений задается своей левой и правой границами, разделенными двоеточием. Соответственно, в диапазоне, открытом слева, отсутствует левая граница, а в диапазоне, открытом справа, – правая граница. По своему смыслу открытые
176
диапазоны аналогичны логическим отношениям «меньше либо равно» и «больше либо равно».
Конструкция CASE может содержать не обязательный блок, начинающийся строкой CASE DEFAULT. В этот блок входят операторы, выполняемые, если все прочие CASE-блоки не сработали. Такой блок не обязательно должен быть последним.
Пример 3.68. Задание селекторов в виде закрытого и открытого диапазона program SUBSTEMP
!Состояние субстанции, как функция температуры. integer TEMPERATURE, ABSNUL, FLAME parameter(ABSNUL = –273, ICE = 0, FLAME = 1000000) read*, TEMPERATURE
select case (TEMPERATURE) case(:–273)
print*, “Давно замерзло все, что можно” case(ABSNUL:ICE)
print*, “Лед” case(FLAME:)
print*, “Пламень” case default
print*, “Туман и сырость” end select
end
3.5.3. Циклы – разновидности конструкции DO
При решении математических задач, связанных с вычислением последовательностей, полиномов и рядов, возникает необходимость вычисления одинаковых по структуре выражений, зависящих от номера элемента, а также суммирования этих выражений, вычисления произведений и т.д. Для программной реализации таких алгоритмов практически во всех императивных языках программирования предусмотрены циклические конструкции. В Фортране 90/95 для этих целей используется конструкция DO.
Наиболее часто используются циклы в виде конструкции DO с фиксированным числом повторений, имеющие общий вид:
DO переменная цикла = нач_знач, кон_знач, шаг_изм блок операторов
END DO
177
В операторе DO указывается переменная цикла, которой при-
сваивается начальное значение и через запятую конечное значение и шаг изменения переменной цикла. В строках, следующих за оператором DO, записываются операторы, выполняемые в цикле. Завершается конструкция цикла оператором END DO.
Переменная цикла – скалярная целая переменная, а ее начальное, конечное значения и шаг изменения должны быть скалярными целыми выражениями. Если шаг изменения переменной цикла равен единице, то его можно не записывать, т.е. первая строка конструкции DO будет выглядеть как:
DO переменная цикла = нач_знач, кон_знач
Число итераций цикла может быть вычислено по формуле:
(кон_знач – нач_знач + шаг_изм)/ шаг_изм)
Если результат меньше нуля, цикл игнорируется. На практике, чтобы цикл был рабочим (не вырожденным) необходимо чтобы при положительном шаге конечное значение переменной было больше начального, а при отрицательном шаге – наоборот: начальное больше конечного.
Следует особо отметить, что в первой строке цикла оператор DO должен быть единственным оператором.
В качестве простейшего примера использования конструкции DO можно рассмотреть вычисление суммы целых чисел от единицы до N (пример 3.69). Здесь переменная цикла I изменяется от единицы до N (введенного с клавиатуры) с шагом «единица» (можно не писать). На каждой из N итераций цикла к изначально нулевой сумме (переменная SUM) прибавляется очередное число.
Пример 3.69. Вычисление суммы целых чисел от единицы до N program SUMN1
integer :: SUM = 0 !Инициализация суммы read*, N
do I = 1, N
SUM = SUM + I end do
print *, “SUM=”, SUM end
178
Этот пример можно модифицировать несколькими способами. 1. Просуммировать числа от N до единицы, заменив
«DO I = 1, N» на «DO I = N, 1, –1».
2.Просуммировать только нечетные числа от единицы до N,
заменив «DO I = 1, N» на «DO I = 1, N, 2».
3.Просуммировать только четные числа от единицы до N, за-
менив «DO I = 1, N» на «DO I = 2, N, 2».
4.Вычислить произведение чисел от единицы до N (факториал числа N, обозначаемый N!), инициализировав переменную SUM не нулем «SUM = 0», а единицей «SUM = 1» и заменив накопление суммы чисел «SUM = SUM + I» накоплением произведения «SUM = SUM * I», можно поменять и имя переменной в соответствии со смыслом новой задачи.
В нотации Фортран 77 (поддерживаемой Фортраном 90/95) цикл начинается оператором DO, в котором после ключевого слова DO сначала указывается метка (например, «100») последнего оператора тела цикла, а затем, как и в конструкции DO, переменная цикла, диапазон ее изменения и шаг. Далее следует тело цикла, завершающееся оператором, помеченным меткой, указанной в операторе DO. Как правило, меткой помечается «пустой» (не вызывающий какого-либо действия) оператор CONTINUE (пример 3.70). Отличительной особенностью цикла DO в Фортране 77 является то, что переменная цикла, ее границы и шаг могут быть не только целыми, но и вещественными, однако от такой экзотической особенности в последующих стандартах было решено отказаться.
Пример 3.70. Вычисление суммы целых чиселв Фортран 77 program SUMN2
integer :: SUM = 0 !Инициализация суммы read*, N
do 100 I = 1, N SUM = SUM + I
100 continue
print *, “SUM=”, SUM end
В стандарты Фортран 90/95 была внесена и тут же отнесена к избыточным и не рекомендованным свойствам языка циклическая конструкция c предусловием DO WHILE, имеющая общий вид:
179
DO WHILE (лог_выр)
блок операторов
END DO
Если при входе в цикл логическое выражение в условии DO WHILE имеет значение ИСТИНА (.TRUE.), то выполняется первая итерация цикла. Проверка осуществляется перед каждой следующей итерацией, и как только выражение будет иметь значение ЛОЖЬ (FALSE), итерации прекращаются. В качестве иллюстрации с помощью конструкции DO WHILE проведено вычисление суммы чисел (пример 3.71), аналогичное примерам 3.69 и 3.70.
Пример 3.71. Вычисление суммы целых чисел с помощью DO WHILE program SUMN3
integer :: SUM = 0, I=0 !Инициализация суммы и переменной цикла read*, N
do while(I < N) I=I+1
SUM = SUM + I end do
print *, “SUM=”, SUM end
Причина отказа от конструкции DO WHILE связана с возможными проигрышами в оптимизации программного кода, проблема подробно описана в гл. 10 работы: Optimizing Supercompilers for Supercomputers, M. Wolfe (Pitman, 1989).
Для управления работой цикла в зависимости от условий рекомендуется конструкция (по сути реализующая бесконечный цикл):
DO
блок операторов
END DO
с включением в тело цикла операторов IF – для контроля условий, а также операторов EXIT – для выхода из текущего цикла и операторов CYCLE – передающих управление на END DO текущего цикла и, таким образом, инициирующих начало следующей итерации цикла. Операторы EXIT и CYCLE, как правило, используются в составе условного оператора IF.
180