Компонентный Паскаль предлагает инструкции выбора IF ELSIF ELSE
. Их можно комбинировать в произвольной последовательности и глубине. Но довольно часто случается, что выбор происходит больше, чем из одной-двух альтернатив. Бородатые компьютерщики помнят, что в начальных меню ДОСа довольно часто, для выбора пункта меню приходилось выбирать номер пункта и вводить его через клавиатуру. Так происходило интерактивное взаимодействие в эпоху чёрных экранов. Если посмотреть на исходники многих современных программ, выбор что надо сделать до сих пор построен именно так (хотя эта логика скрыта плюшками, приблудами и контролами интерфейса графического пользователя).
Так когда же нужно применять оператор выбора CASE
? Должно быть выполнено два условия:
Как использовать оператор CASE
представлено ниже:
MODULE Test_case; IMPORT Log; PROCEDURE SelectV (v: INTEGER); BEGIN Log.String("v:="); Log.Int(v); Log.Ln; CASE v OF 1..10: Log.String("Малое число"); Log.Ln; | 11..100: Log.String("Среднее число"); Log.Ln; | 101..1000: Log.String("Ого! Большое число!"); Log.Ln; ELSE Log.String("Нет. Это что-то не то..."); Log.Ln; END; END SelectV; PROCEDURE Start*; VAR v: INTEGER; BEGIN v := 6; SelectV(v); v := 12; SelectV(v); v := 102; SelectV(v); v := - 1; SelectV(v); END Start; END Test_case.
Вывод:
компилируется "Test_case" 252 0 старый модуль Test_case выгружен v := 6 Малое число v := 12 Среднее число v := 102 Ого! Большое число! v := - 1 Нет. Это что - то не то...
Как видно из текста модуля, количество веток с ELSE
было бы драматически большим, пришлось бы написать больше кода, а значит риск совершения ошибки увеличивается. Кроме того, более компактная форма записи, позволяет сократить занимаемое место на экране, что положительно сказывается на понимании текста программы.
Таких слов на данный момент рассмотрено три:
MODULE
[NameModule]; – обязательное ключевое слово с именем модуля. По другому компилятор никак не сможет определить к чему относится программный текст.END
[NameModule]. – обязательное ключевое слово с именем модуля. По другому компилятор никак не сможет определить, где заканчивается программный код. Нельзя забывать, что за концом программного текста ещё могут быть различные элементы, как-то: коммандер (а то и не один), данные для ввода, данные для компиляции, заметки типа TODO или краткие примеры правильного использования. Кроме того, можно вставить ссылки на документацию по модулю или смежные темы.BEGIN
– необязательное ключевое слово уровня модуля. Обычно, код размещённый после него – исполняется для служебных целей во время загрузки самого модуля. По возможности, не стоит использовать это логическое пространство для кода. С высокой вероятностью, это будет плохим решением.CLOSE
– необязательное ключевое слово уровня модуля. Обычно, код размещённый после него – исполняется для служебных целей во время выгрузки самого модуля. Эта возможность используется ещё реже, чем BEGIN
, так что с ещё большей вероятностью использование этой секции будет ещё более плохим решением.
Читатели знакомые, например, с python, обратят своё внимание, что это очень похоже на специальные методы классов init
и del
. И такое сходство будет не случайно. Вспоминаем, что основная единица компиляции в КП – модуль! И понятие модуля органически совмещается с понятием класса. И всё же, надо помнить, что BEGIN
и CLOSE
относятся к модулю, а не классу (которых в python в одном модуле может быть несколько, хотя сами же питонёры к такой практике относятся не очень хорошо).
Здесь будут рассмотрены только обойдённые вниманием операции.
MOD
– бывает весьма полезная операция для поиска остатка при целочисленном делении. Можно вычислить остаток и через программное решение, но оно гарантированно будет более медленней. Пример: 5 MOD 3 = 2
.DIV
– также весьма полезная операция для целочисленного деления (т. е. когда оба операнда имеют целочисленный тип, и результат имеет его же). Также работает заметно быстрее, чем операции над вещественными числами. Пример: 5 DIV 3 = 1
. Разумеется, точность страдает, но бывает это приемлемо.~[число]
– выворачивает число. Инвертирует все биты. OR
– логическое сложение чисел, бывает полезным, например, при наложении изображений. Пример: 3 OR 2 = 3
.&
– логическое умножение (читается «амперсанд»). Бывает полезным при, например: шифровании, обработки изображений. Пример: 5 & 3 = 1
.IN
– принадлежность к множеству. Не путать с параметрами на вход в процедурах! Пример: 6 in a
. Где а: ARRAY 1000 OF BYTE
(или ещё чего-нибудь). Если значение 6 содержится в массиве а
, то результатом этого выражения будет TRUE
, в противном случае FALSE
.IS
– принадлежность к типу. Бывает полезным узнать во время выполнения программы к какому типу относится переменная. Используется вместо с оператором выбора типа WITH
. Аналогично с предыдущим ключевым словом результатом вычисления является TRUE
или FALSE
. Оператор IS
вводит в Компонентный Паскаль возможности интроспекции (исследование «системы во внутрь»).ASSERT
– оператор охраны. Вызывает системное исключение, если выражение скобках не соответствует истине. Например, ASSERT( (10<i) & (i<20), 20)
. Системное исключение возникнет тогда, когда i
будет меньше или равно 10 и больше или равно 20.Этот оператор занимает промежуточное положение между оператором контекста, оператором приндлежности к типу и оператором множественного выбора. В динамических средах бывает сложно предугадать тип «подсовываемой» переменной. И в зависимости от типа, нужно выполнять различные действия.
Например:
MODULE Test_case; IMPORT Log; TYPE tRecord = EXTENSIBLE RECORD x: INTEGER; y: INTEGER; END; tRecord1 = RECORD (tRecord) z: INTEGER; END; PROCEDURE SelectV (VAR v: tRecord); BEGIN WITH v: tRecord1 DO Log.String("Это тип tRecord1"); Log.Ln; | v: tRecord DO Log.String("Это тип tRecord"); Log.Ln; ELSE Log.String("Нет. Это что-то не то..."); Log.Ln; END; END SelectV; PROCEDURE Start*; VAR v: POINTER TO tRecord; v1: POINTER TO tRecord1; BEGIN NEW(v); NEW(v1); v.x := 6; SelectV(v); v1.z := 3; SelectV(v1); END Start; END Test_case.
Вывод:
компилируется "Test_case" 176 0 старый модуль Test_case выгружен Это тип tRecord Это тип tRecord1
В приведённом примере создаётся тип tRecord
и наследующий его tRecord1
. Процедура SelectV
принимает только тип tRecord
, и, теоретически, компилятор должен был грязно выругаться при попытке скомпилировать этот пример. Ведь в процедуре Start
видна попытка скормить для SelectV
переменную типа tRecord1
(а это уже другой тип). Но компилятор соглашается!!!
После этого в процедуре SelectV
использована небольшая хитрость. Так как одна из переменных имеет тип tRecord1
, то при первом вызове эта ветка будет пропущена (и это правильно, так как тип переменной – tRecord
). А во втором случае выполнение пойдёт именно по первой ветке (и это тоже правильно, так как второй тип – tRecord1
является расширением tRecord
). После выполнения одной из веток – другие ветки выполняться не будут.
В тоже время, необходимо обратить внимание, что если в коде попытаться передать переменную типа INTEGER
(или вообще любую, не наследующую и не являющуюся типом tRecord
) – компилятор просто откажется компилировать такой модуль.
Приведённые особенности Компонентного Паскаля надо знать. Можно поспорить с тем, что, например CASE
может и излишняя семантическая конструкция в языке, но опытные программисты на практическом примере покажут вам преимущества того или иного способа. Важно иметь практику, и понимание, что для чего предназначено.
В качестве самостоятельного задания, которое потребует почитать документацию, рекомендуется составить две программы с помощью IF THEN ELSE
и CASE
, и сравнить быстродействие на 10 ветках исполнения 1).