Оператор ветвления

Материал из Seo Wiki - Поисковая Оптимизация и Программирование

Перейти к: навигация, поиск

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

Содержание

Общее описание

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

Виды условных инструкций

Существует две основные формы условной инструкции, встречающиеся в реальных языках программирования: условный оператор (оператор if) и оператор многозначного выбора (переключатель, case, switch).

Условный оператор

Условный оператор реализует выполнение определённых команд при условии, что некоторое логическое выражение (условие) принимает значение «истина» true. В большинстве языков программирования условный оператор начинается с ключевого слова if.

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

Условный оператор с одной ветвью
if условие then команды end
При выполнении такого оператора вычисляется условие, и если оно истинно, то выполняются команды до ключевого слова end, в противном случае выполнение программы продолжается со следующей за условным оператором команды. В языках низкого уровня (ассемблерах) это — единственная доступная форма условного оператора. В некоторых языках для условного оператора с одной ветвью используется специальное ключевое слово (обычно это when).
Условный оператор с двумя ветвями
if условие then команды1 else команды2 end
Здесь при истинности условия выполняются команды1 при ложности — команды2. При необходимости проверить последовательно несколько условий возможно каскадирование условных операторов:
if условие1 
  then команды1 
  else if условие2 then команды2
    else if условие3 then команды3
        ... 
          else if условиеN-1 then командыN-1
            else командыN end;
В этом случае условия будут проверяться последовательно, и как только встретится истинное, будет выполнен соответствующий набор команд и исполнение перейдёт к команде, следующей за условным оператором. Если ни одно из условий не окажется истинным, выполнятся командыN из ветви else.
Условный оператор с несколькими условиями
Вышеприведённая схема каскада условных операторов используется достаточно часто, поэтому ряд языков программирования содержит специальную конструкцию для неё, позволяющую записать множественное ветвление несколько компактнее и менее подверженную ошибкам написания:
if условие1 then команды1
elsif условие2 then команды2
...
else командыN end;
порядок выполнения этого оператора в точности соответствует вышеприведённому каскаду простых операторов if-then-else.

Реализация в Pascal

Паскаль унаследовал от Алгола-60 синтаксис, согласно которому в ветвях условного оператора может быть помещена только одна команда. Поэтому для размещения там большего количества команд они группируются в составной оператор с помощью пары ключевых слов begin и end. Ветвь else в условном операторе не является обязательной.

If условие Then
begin
  операторы;
end
else
begin
  операторы;
end;

Реализация в C++

C и C++ (а вслед за ними и Java, C#, PHP и множество других языков) имеют условный оператор, структурно аналогичный Паскалю. Отличие состоит в том, что условие должно быть записано в круглых скобках, а вместо ключевых слов begin и end используются фигурные скобки {} :

if (<условие>)
{
<операторы>
}
else
{
<операторы>
}

Nemerle

Интересны условные операторы языка Nemerle. В отличие от большинства языков, где оператор if может иметь как одну, так и две ветви, в Nemerle оператор if (синтаксис полностью аналогичен языку Си) обязан иметь две ветви. Условный оператор с одной ветвью начинается с ключевого слова when, кроме того, в языке имеется ещё один условный оператор — unless, представляющий собой «обратный when» — в нём команды условной ветви выполняются, если условие ложно.

Переключатель

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

Прототипом современной синтаксической конструкции была используемая в старых языках программирования команда перехода по вычисляемой метке. В этой команде указывалось выражение-селектор, возвращающее целое значение, и набор меток. При выполнении команды вычислялось выражение, а его значение использовалось как номер метки (в списке команды), на которую производился переход. Такие конструкции были, например, в языках программирования Фортран и Бейсик. Привлекательной стороной конструкции является её достаточно высокая эффективность: для определения нужной ветви (метки перехода) не требуется последовательно сравнивать результат выражения-селектора со многими занчениями, достаточно записать в память массив команд безусловного перехода с нужными адресами, чтобы при выполнении команды вычислять нужный элемент непосредственно из значения выражения. При этом скорость выполнения команды не зависит от количества меток. В современных языках реализация оператора-переключателя также часто выполняется в виде таблицы перехода, состоящей из команд безусловного перехода на соответствующие фрагменты кода. Вычисляемое выражение преобразовывается в значение сдвига по таблице перехода, определяющее выполняемую команду. В языках, где выражение-селектор может иметь нецелое значение, напрямую вычислить нужную ветвь конструкции переключателя можно далеко не всегда, поэтому в них используются другие методы оптимизации исполнения.

В современных языках программирования высокого уровня команда-переключатель обычно имеет имя switch либо case. Однако, выбор по вычисляемой метке может сохраняться в современных языках программирования низкого уровня, например, инструкция JL языка программирования STL для программируемых логических контроллеров S7-300 и S7-400, выпускаемых Siemens

Например, в языке Си синтаксис команды следующий:

switch (i)
{
case 0:
case 1: // последовательность операторов
    break;
case 2: // последовательность операторов
    break;
default:
}

Здесь i — выражение-селектор, которое обязано иметь приводимый к целому тип, каждая ветвь исполнения начинаются с ключевого слова case, за ним следует значение выражения, при котором должна выполняться данная ветвь. Интересной особенностью языка Си является то, что в нём переключатель трактуется именно как команда перехода по вычисляемой метке, а роль меток играют заголовки ветвей (case значение :). Чтобы после завершения кода ветви произошёл выход из оператора переключателя, используется специальная команда break. Если такой команды в ветви нет, после исполнения кода выбранной ветви начнётся исполнение кода следующей за ней. Эта особенность может использоваться для оптимизации, хотя может служить причиной труднообнаруживаемых ошибок (если программист случайно пропустит break, компилятор не выдаст ошибки, но программа будет выполняться неверно). Ветвь default исполняется тогда, когда среди прочих ветвей не нашлось ни одной подходящей.

Синтаксис команды-переключателя Си унаследован множеством языков, но семантика его не всегда аналогична Си. Например, в Java конструкция имеет точно такой же вид, но любая ветвь, кроме default, обязана заканчиваться на break — отсутствие этого ключевого слова является синтаксической ошибкой. В C# допускается использовать выражение-селектор строкового типа и соответствующие метки.

Особенности вычисления логических выражений

На логику исполнения программы с условными операторами может существенно влиять принятая в языке логика вычисления условных выражений. Если утверждение представляет собой сложное логическое утверждение, к примеру «f(x) > 0 и g(y) > 0», существует две стратегии

  • Полное вычисление: вычислится f(x) и g(y), затем произойдет сравнение результатов с нулём, затем вычислится операция «и» для результатов. Так поступает, к примеру, Visual Basic.
  • Неполное вычисление: вычислится f(x), произойдет сравнение с нулем. Если оно неверно, то второе условие, в том числе входящее в него g(y), вычисляться не будет, так как выражение заведомо ложно.

Второй вариант является наиболее распространённым для промышленных языков (в частности, для Алгола, Фортрана, С++, С, Java, JavaScript, ECMAScript, JScript, C#). В этих языках действует жёсткое правило: «Логическое выражение всегда вычисляется слева направо и его вычисление останавливается сразу же, как только результат всего выражения становится определённым». Это означает, что если выражение состоит из нескольких подусловий, объединённых оператором «И» (AND), то вычисление выражения прекратится, как только одно из подусловий окажется ложным (так как «ложь AND любое значение» в результате всегда даёт «ложь»), и, наоборот, если несколько подусловий объединены оператором «ИЛИ» (OR), вычисление прекратится после первого же истинного подусловия, поскольку в этом случае всё выражение истинно, независимо от дальнейших вычислений. А вот выражение, содержащее оператор «Исключающее ИЛИ» (XOR) неполному вычислению не поддаётся, поскольку в нём одно из значений не может определить результат вычисления всего выражения.

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

// Поиск в массиве целых чисел на языке Паскаль
// Параметры - искомое значение и открытый массив целых чисел
// Результат - индекс найденного элемента или -1 в случае, если элемент не найден
function Find(e: integer; var a: array of integer): integer;
var
  i: integer;
begin
  i := 0;
  while (i <= High(a)) AND (a[i] <> e) do inc(i); // !!!
  if i <= High(a) 
    then Find := i
    else Find := -1;
end;

Алгоритм, реализуемый программой, совершенно очевиден, но в реализации есть одна тонкость (см. строку, помеченную восклицательными знаками): условие цикла состоит из двух частей, связанных оператором AND. Первое подусловие проверяет, не вышел ли индекс i за пределы массива, второе — не равен ли текущий элемент массива искомому значению. Если массив не содержит искомого значения, то после проверки последнего элемента значение переменной i увеличится на единицу. Если после этого будут вычисляться ОБА подусловия, то второе приведёт к ошибке — ведь i уже вышло за границы массива. Чтобы избежать этого эффекта, пришлось бы записать gоиск примерно в следующем виде:

// Поиск в массиве целых чисел на языке Паскаль
// Гипотетический вариант при полном вычислении логических выражений
// Параметры - искомое значение и открытый массив целых чисел
// Результат - индекс найденного элемента или -1 в случае, если элемент не найден
function Find(e: integer; var a: array of integer): integer;
var
  i: integer;
  f: boolean; // дополнительная переменная - флаг завершения цикла
begin
  i := 0;
  f := false;
  while not f do
    if i > High(a) then f := true
    else if a[i] = e then f := true
    else inc(i); 
  if i <= High(a) 
    then Find := i
    else Find := -1;
end;

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

См. также


Ссылки


de:Bedingte Anweisung

en:Conditional (programming) es:Sentencia condicional he:פקודת if hr:Uvjetna naredba hu:Feltételes utasítás ja:If文 pl:Instrukcja warunkowa pt:Estrutura de seleção uk:Умовний перехід

Личные инструменты

Served in 0.166 secs.