Операторы и поток управления#

Одним из самых больших преимуществ компьютерных алгоритмов, по сравнению с простыми математическими формулами, является возможность ветвления программы, когда программа может решать, какие инструкции выполнять дальше, основываясь на логическом условии.

Существуют две основных формы управления потоком программы:

  • Условный (if): выбор ветки исполнения программы на основе логического значения (true или false – истина или ложь)

  • Цикл: повторение части кода несколько раз

Логические операторы#

Прежде чем использовать оператор условного ветвления, необходимо сформировать логическое выражение.

Для формирования логического выражения, доступен следующий набор операторов отношения:

Оператор  

Альтернативная форма записи  

Описание

==

.eq.

Проверка равенства двух операндов

/=

.ne.

Проверка неравенства двух операндов

>

.gt.

Проверка на то, что левый операнд строго больше правого операнда

<

.lt.

Проверка на то, что левый операнд строго меньше правого операнда

>=

.ge.

Проверка на то, что левый операнд больше или равен правому операнду

<=

.le.

Проверка на то, что левый операнд меньше или равен правому операнду


а также следующие логические операторы:

Оператор  

Описание

.and.

TRUE (истина) если оба операнда (левый и правый) возвращают значение TRUE (истина)

.or.

TRUE (истина) если либо левый, либо правый, либо оба операнда возвращают значение TRUE (истина)

.not.

TRUE (истина) если правый операнд возвращает значение FALSE (ложь)

.eqv.

TRUE (истина) если левый операнд имеет то же логическое значение, что и правый операнд

.neqv.

TRUE (истина) если левый операнд противоположное логическое значение по сравнению с правым операндом


Условная конструкция (if)#

В следующих примерах условная конструкция if используется для вывода сообщения для описания характера переменной angle (угол):

Пример: одиночная ветвь if

if (angle < 90.0) then
  print *, 'Angle is acute'
end if

В этом первом примере код внутри конструкции if выполняется только если тестовое выражение (angle < 90.0) истинно.

Совет

Хорошей практикой считается добавление отступа внутри таких конструкций как if и do, чтобы сделать код более читабельным.

Мы можем добавить альтернативную ветвь в конструкцию с помощью ключевого слова else:

Example: две ветви if-else

if (angle < 90.0) then
  print *, 'Angle is acute'
else
  print *, 'Angle is obtuse'
end if

Теперь в конструкции if есть две ветви, но выполнятся только одна ветвь в зависимости от логического выражения, следующего за ключевым словом if.

Мы можем добавить любое количество ветвей с помощью ключевого слова else if, чтобы задать больше условий:

Пример: множественное ветвление if-else if-else

if (angle < 90.0) then
  print *, 'Angle is acute'
else if (angle < 180.0) then
  print *, 'Angle is obtuse'
else
  print *, 'Angle is reflex'
end if

При использовании нескольких условных выражений, каждое условное выражение проверяется только в том случае, если ни одно из предыдущих не было истинно.

Конструкции циклов (do)#

В следуем примере конструкция цикла do используется для вывода последовательности чисел. Цикл do имеет целочисленную переменную счётчик (счётчик цикла), которая используется для отслеживания того, какая итерация цикла выполняется в данный момент. В этом примере мы используем принятое обычно для такой переменной счётчика имя: i.

Когда мы определяем начало цикла do, мы используем имя нашей переменной счётчика, за которой следует знак присваивания (=), чтобы указать начальное и конечное значение нашего счётчика цикла.

Пример: цикл do

integer :: i

do i = 1, 10
  print *, i
end do

Пример: цикл do с заданием шага счётчика

integer :: i

do i = 1, 10, 2
  print *, i  ! Print odd numbers
end do

Цикл с условием (do while)#

В цикл do можно добавить проверку условия с помощью ключевого слова while. Тогда цикл будет выполняться до тех пор, пока условие, заданное в while(), истинно (.true.).

Пример: цикла do while()

integer :: i

i = 1
do while (i < 11)
  print *, i
  i = i + 1
end do
! Here i = 11

Операторы управления циклом (exit and cycle)#

Часто бывает необходимо остановить выполнение цикла при выполнении какого-либо условия. Fortran предоставляет для таких случаев два оператора исполнения.

exit (выход) используется для преждевременного завершения цикла. Он обычно используется внутри условной конструкции if.

Пример: цикл с оператором exit

integer :: i

do i = 1, 100
  if (i > 10) then
    exit  ! Stop printing numbers
  end if
  print *, i
end do
! Here i = 11

С другой стороны, оператор cycle принудительно заставляет программу пропустить выполнение всего, что осталось ниже него и перейти к выполнению следующей итерации цикла.

Пример: цикл с оператором cycle

integer :: i

do i = 1, 10
  if (mod(i, 2) == 0) then
      cycle  ! Don't print even numbers
  end if
  print *, i
end do

Примечание

При использовании внутри вложенных циклов операторы cycle и exit действуют на самый внутренний цикл внутри которого находятся.

Управление вложенным циклом: метки#

В любом языке программирования часто встречается использование вложенных циклов. Вложенные циклы – циклы, которые выполняются внутри другого цикла. Fortran позволяет программисту пометить или назвать каждый цикл меткой или именем. Использование меток для циклов, даёт два потенциальных преимущества:

  1. Может быть улучшена читабельность кода (при условии, что имена меток осмысленные).

  2. Операторы exit и cycle могут использоваться с метками, что позволяет очень тонко управлять переходами в циклах.

Пример: вложенные циклы с метками

integer :: i, j

outer_loop: do i = 1, 10
  inner_loop: do j = 1, 10
    if ((j + i) > 10) then  ! Print only pairs of i and j that add up to 10
      cycle outer_loop  ! Go to the next iteration of the outer loop
    end if
    print *, 'I=', i, ' J=', j, ' Sum=', j + i
  end do inner_loop
end do outer_loop

Параллельное выполнение цикла (do concurrent)#

Оператор цикла do concurrent используется для явного указания, что внутри цикла нет взаимозависимостей; он сообщает компилятору, что возможно использовать распараллеливание/SIMD для ускорения выполнения цикла и более явно передаёт намерения программиста. Более конкретно, это означает, что любая отдельная итерация цикла не зависит от выполнения предшествующих итераций этого цикла. Также необходимо, чтобы любые изменения состояния, которые могут произойти, происходили только внутри каждой итерации цикла do concurrent. Эти требования накладывают ограничения на то, что может быть размещено в теле такого цикла.

Простая замена цикла do на цикл do concurrent не гарантирует параллельного выполнения. Приведённое выше объяснение не содержит всех требований, которые должны быть выполнены для написания корректного цикла do concurrent. Компиляторы также вольны поступать по своему усмотрению, то есть они могут не оптимизировать цикл (например, при небольшом количестве итераций для выполнения простого вычисления, как в примере ниже). В общем случае для активации возможного распараллеливания циклов do concurrent требуются использование дополнительных флагов компиляции.

Пример: цикл do concurrent()

real, parameter :: pi = 3.14159265
integer, parameter :: n = 10
real :: result_sin(n)
integer :: i

do concurrent (i = 1:n)  ! Careful, the syntax is slightly different
  result_sin(i) = sin(i * pi/4.)
end do

print *, result_sin