Опубликован: 17.10.2005 | Уровень: специалист | Доступ: свободно
Лекция 13:

Поддерживающие механизмы

Инструкции

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

Для имеющих опыт работы с современными языками инструкции выглядят как хорошие знакомые. Исключение составляют некоторые специальные свойства циклов, облегчающие их верификацию. Вот список инструкций: Вызов процедуры, Присваивание, Условие, Множественный выбор, Цикл, Проверка, Отладка, Повторное выполнение, Попытка присваивания.

Вызов процедуры

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

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

r (без аргументов), или

r (x, y, ...) (с аргументами)

Квалифицированный вызов явно называет свою цель, заданную некоторым выражением. Если a - выражение некоторого типа, C - базовый класс этого типа, а - q одна из программ C, то квалифицированный вызов имеет форму a.q. Опять же, за q может следовать список фактических аргументов; a может быть неквалифицированным вызовом функции с аргументами, как в p (m).q (n), где p(m) - это цель. В качестве цели можно также использовать более сложное выражение при условии заключения его в скобки, как в (vector1 + vector2).count.

Также разрешаются квалифицированные вызовы с многоточием в форме: a.q1q2 ...qn, где a, так же, как и qi, может включать список фактических аргументов.

Экспорт управляет применением квалифицированных вызовов. Напомним, что компонент f, объявленный в классе B, доступен в классе A ( экспортирован классу ), если предложение feature, объявляющее f, начинается с feature (без дальнейшего уточнения) или feature {X, Y,... }, где один из элементов списка {X, Y,...} является A или предком A. Имеет место:

Правило Квалифицированного Вызова

Квалифицированный вызов вида b.q1. q2.... qn, появляющийся в классе C корректен, только если он удовлетворяет следующим условиям:

  1. Компонент, стоящий после первой точки, q1, должен быть доступен в классе C.
  2. В вызове с многоточием, каждый компонент после второй точки, то есть каждое qi для i > 1, должен быть доступен в классе C.

Чтобы понять причину существования второго правила, отметим, что a.q.r.s - краткая запись для

b:= a.q; c:=b.r; c.s

которая верна только, если q, r и s доступны классу C, в котором появляется этот фрагмент. Не имеет значения, доступно ли r базовому классу типа q, и доступно ли s базовому классу типа r.

Вызовы могут иметь инфиксную или префиксную форму. Выражение a + b, записанное в инфиксной форме, может быть переписано в префиксной форме: a.plus (b). Для обеих форм действуют одинаковые правила применимости.

Присваивание (Assignment)

Инструкция присваивания записывается в виде:

x := e

где x - сущность, допускающая запись (writable), а e - выражение совместимого типа. Такая сущность может быть:

  • неконстантным атрибутом включающего класса;
  • локальной сущностью включающей подпрограммы. Для функции допустима сущность Result.

Сущности, не допускающие запись, включают константные атрибуты и формальные аргументы программы - которым, как мы видели, подпрограмма не может присваивать новое значение.

Создание (Creation)

Инструкция создания изучалась в предыдущих лекциях3См. "Инструкция создания" и "Процедуры создания", "Динамические структуры: объекты" . Один из вариантов рассмотрен в "Полиморфное создание", "Введение в наследование" . в двух ее формах: без процедуры создания, как в create x, и с процедурой создания, как в create x.p (...). В обоих случаях x должна быть сущностью, допускающей запись.

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

Эта инструкция задает различные формы обработки в зависимости от выполнения определенных условий. Основная форма:

if boolean_expression then
   instruction; instruction; ...
else
   instruction; instruction; ...
end

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

Будут выполняться инструкции первой ветви, если boolean_expression верно, а иначе - второй ветви. Можно опустить часть else, если второй список инструкций пуст, что дает:

if boolean_expression then
   instruction; instruction; ...
end

Когда есть более двух возможных случаев, можно избежать вложения (nesting) условных команд в частях else, используя одну или более ветвей elseif, как в:

if c1 then
   instruction; instruction; ...
elseif c2 then
   instruction; instruction; ...
elseif c3 then
   instruction; instruction; ...
...
else
   instruction; instruction; ...
end

где часть else остается факультативной. Это дает возможность избежать вложения

if c1 then
   instruction; instruction; ...
else
   if c2 then
      instruction; instruction; ...
   else
      if c3 then
         instruction; instruction; ...
...
      else
         instruction; instruction; ...
      end
   end
end

Когда необходим множественный разбор случаев, более удобна инструкция множественного выбора inspect, обсуждаемая ниже.

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

Множественный выбор

Инструкция множественного выбора (также известная, как инструкция Case ) производит разбор вариантов, имеющих форму: e = vi, где e - выражение, а vi - константы того же типа. Хотя условная инструкция (if e = v1 then ...elseif e = v2 then...) работает, есть две причины, оправдывающие применение специальной инструкции, что является исключением из обычного правила: "если нотация дает хороший способ сделать что-то, нет необходимости вводить другой способ". Вот эти причины:

  • Разбор случаев настолько распространен, что заслуживает особого синтаксиса, увеличивающего ясность, позволяя избежать бесполезного повторения " e = ".
  • Компиляторы могут использовать особенно эффективную технику реализации, - таблицу переходов ( jump table ), - неприменимую к общим условным инструкциям и избегающую явных проверок.

Что касается типа анализируемых величин (тип e и vi ), то инструкции множественного выбора достаточно поддерживать только целые и булевы значения. Согласно правилу, они фактически должны объявляться либо все как INTEGER, либо как CHARACTER. Общая форма инструкции такова:

inspect
   e
when v1 then
   instruction; instruction; ...
when v2 then
   instruction; instruction; ...
...
else
   instruction; instruction; ...
end

Все значения vi должны быть различными; часть else факультативна; каждая из ветвей может иметь произвольное число инструкций или не иметь их.

Инструкция действует так: если значение e равно значению vi (это может быть только для одного из них), выполняются инструкции соответствующей ветви; иначе, выполняются инструкции в ветви else, если они есть.

Если отсутствует else, и значение e не соответствует ни одному vi, то возникает исключительная ситуация ("Некорректно проверяемое значение"). Это решение может вызвать удивление, поскольку соответствующая условная инструкция в этом случае ничего не делает. Но оно характеризует специфику инструкции множественного выбора. Когда вы пишете inspect с набором значений vi, нужно включить ветвь else, даже пустую, если вы понимаете, что во время выполнения значения e могут не соответствовать никаким vi. Если вы не включаете else, то это эквивалентно явному утверждению: "значение e всегда является одним из vi ". Проверяя это утверждение и создавая исключительную ситуацию при его нарушении, реализация оказывает нам услугу. Бездействие в данной ситуации - означает ошибку - в любом случае, ее необходимо устранить как можно раньше.

Одно из частых приложений инструкции множественного выбора - анализ символа, введенного пользователем4Это элементарная схема. О более сложных технических приемах обработки пользовательских команд см. лекцию 3 курса "Основы объектно-ориентированного проектирования". :

inspect
   first_input_letter
when 'D' then
   "Удалить строку"
when 'I' then
   "Вставить строку"
...
else
   message ("Неопознанная команда; введите H для получения справки")
end

Когда значения vi целые, то они могут быть определены как уникальные (unique values), концепция которых рассмотрена в следующей лекции. Это делает возможным в объявлении определить несколько абстрактных констант, например, Do, Re, Mi, Fa, Sol, La, Si: INTEGER is unique, и затем анализировать их в инструкции: inspect note when Do then...when Re then...end.

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

Циклы

Синтаксис циклов описан при обсуждении Проектирования по Контракту ( "Проектирование по контракту: построение надежного ПО" ):

from
   initialization_instructions
invariant
   invariant
variant
   variant
until
   exit_condition
loop
   loop_instructions
end

Предложения invariant и variant факультативны. Предложение from требуется, хотя и может быть пустым. Оно задает инициализацию параметров цикла. Не рассматривая сейчас факультативные предложения, выполнение цикла можно описать следующим образом. Вначале происходит инициализация, и выполняются initialization_instructions. Затем следует "циклический процесс", определяемый так: если exit_condition верно, то циклический процесс - пустая инструкция ( null instruction ); если условие неверно, то циклический процесс - это выполнение loop_instructions, затем следует (рекурсивно) повторение циклического процесса.

Проверка

Инструкция проверки рассматривалась при обсуждении утверждений ( "Проектирование по контракту: построение надежного ПО" ). Она говорит, что определенные утверждения должны удовлетворяться в определенных точках:

check
   assertion -- Одно или больше предложений
end

Отладка

Инструкция отладки является средством условной компиляции. Она записывается так:

debug instruction; instruction; ... end

В файле управления (Ace-файле) для каждого класса можно включить или отключить параметр debug. При его включении все инструкции отладки данного класса выполняются, при отключении - они не влияют на выполнение.

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

Повторение вычислений

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

Александр Шалухо
Александр Шалухо
Как сбросить прогресс по курсу? Хочу начать заново
Анатолий Садков
Анатолий Садков
Вопросик
Александр Качанов
Александр Качанов
Япония, Токио
Янош Орос
Янош Орос
Украина, Киев