|
|
bb:redbook:213 [2016/09/08 10:16] prospero78 [3. Арифметические и логические операции] |
bb:redbook:213 [2018/11/30 01:13] |
===== 2.13 Специфичные ключевые слова ===== | |
| |
==== 1. Оператор выбора CASE ==== | |
Компонентный Паскаль предлагает инструкции выбора ''IF ELSIF ELSE''. Их можно комбинировать в произвольной последовательности и глубине. Но довольно часто случается, что выбор происходит больше, чем из одной-двух альтернатив. Бородатые компьютерщики помнят, что в начальных меню ДОСа довольно часто, для выбора пункта меню приходилось выбирать номер пункта и вводить его через клавиатуру. Так происходило интерактивное взаимодействие в эпоху чёрных экранов. Если посмотреть на исходники многих современных программ, выбор //что надо сделать// до сих пор построен именно так (хотя эта логика скрыта //плюшками//, //приблудами// и //контролами// интерфейса графического пользователя). | |
| |
Так когда же нужно применять оператор выбора ''CASE''? Должно быть выполнено два условия: | |
| |
- Выбор надлежит сделать из более чем двух величин. | |
- Выбираемые величины должны быть однотипными -- целыми или литерными. | |
| |
Как использовать оператор ''CASE'' представлено ниже: | |
<code oberon2> | |
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. | |
</code> | |
Вывод: | |
<code> | |
компилируется "Test_case" 252 0 | |
старый модуль Test_case выгружен | |
v := 6 | |
Малое число | |
v := 12 | |
Среднее число | |
v := 102 | |
Ого! Большое число! | |
v := - 1 | |
Нет. Это что - то не то... | |
</code> | |
| |
Как видно из текста модуля, количество веток с ''ELSE'' было бы драматически большим, пришлось бы написать больше кода, а значит риск совершения ошибки увеличивается. Кроме того, более компактная форма записи, позволяет сократить занимаемое место на экране, что положительно сказывается на понимании текста программы. | |
| |
| |
==== 2. Ключевые слова модуля ==== | |
Таких слов на данный момент рассмотрено три: | |
- ''MODULE'' [NameModule]; -- обязательное ключевое слово с именем модуля. По другому компилятор никак не сможет определить к чему относится программный текст. | |
- ''END'' [NameModule]. -- обязательное ключевое слово с именем модуля. По другому компилятор никак не сможет определить, где заканчивается программный код. Нельзя забывать, что за концом программного текста ещё могут быть различные элементы, как-то: коммандер (а то и не один), данные для ввода, данные для компиляции, заметки типа **TODO** или краткие примеры правильного использования. Кроме того, можно вставить ссылки на документацию по модулю или смежные темы. | |
- ''BEGIN'' -- необязательное ключевое слово уровня модуля. Обычно, код размещённый после него -- исполняется для служебных целей во время загрузки самого модуля. По возможности, не стоит использовать это логическое пространство для кода. С высокой вероятностью, это будет плохим решением. | |
- ''CLOSE'' -- необязательное ключевое слово уровня модуля. Обычно, код размещённый после него -- исполняется для служебных целей во время выгрузки самого модуля. Эта возможность используется ещё реже, чем ''BEGIN'', так что с ещё большей вероятностью использование этой секции будет ещё более плохим решением. | |
| |
Читатели знакомые, например, с **python**, обратят своё внимание, что это очень похоже на специальные методы классов ''__init__'' и ''__del__''. И такое сходство будет не случайно. Вспоминаем, что основная единица компиляции в КП -- //модуль//! И понятие //модуля// органически совмещается с понятием //класса//. И всё же, надо помнить, что ''BEGIN'' и ''CLOSE'' относятся к модулю, а не классу (которых в **python** в одном модуле может быть несколько, хотя сами же питонёры к такой практике относятся не очень хорошо). | |
| |
| |
==== 3. Арифметические и логические операции ==== | |
Здесь будут рассмотрены только обойдённые вниманием операции. | |
- ''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. | |
| |
| |
==== 4. Оператор WITH ==== | |
Этот оператор занимает промежуточное положение между оператором контекста, оператором приндлежности к типу и оператором множественного выбора. В динамических средах бывает сложно предугадать тип "подсовываемой" переменной. И в зависимости от типа, нужно выполнять различные действия. | |
| |
Например: | |
<code oberon2> | |
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. | |
</code> | |
Вывод: | |
<code> | |
компилируется "Test_case" 176 0 | |
старый модуль Test_case выгружен | |
Это тип tRecord | |
Это тип tRecord1 | |
</code> | |
| |
| |
В приведённом примере создаётся тип ''tRecord'' и наследующий его ''tRecord1''. Процедура ''SelectV'' принимает только тип ''tRecord'', и, теоретически, компилятор должен был грязно выругаться при попытке скомпилировать этот пример. Ведь в процедуре ''Start'' видна попытка скормить для ''SelectV'' переменную типа ''tRecord1'' (а это уже другой тип). Но компилятор соглашается!!! | |
| |
После этого в процедуре ''SelectV'' использована небольшая хитрость. Так как одна из переменных имеет тип ''tRecord1'', то при первом вызове эта ветка будет пропущена (и это правильно, так как тип переменной -- ''tRecord''). А во втором случае выполнение пойдёт именно по первой ветке (и это тоже правильно, так как второй тип -- ''tRecord1'' является расширением ''tRecord''). После выполнения одной из веток -- другие ветки выполняться не будут. | |
| |
В тоже время, необходимо обратить внимание, что если в коде попытаться передать переменную типа ''INTEGER'' (или вообще любую, не наследующую и не являющуюся типом ''tRecord'') -- компилятор просто откажется компилировать такой модуль. | |
| |
| |
==== 5. Заключение ==== | |
Приведённые особенности **Компонентного Паскаля** надо знать. Можно поспорить с тем, что, например ''CASE'' может и излишняя семантическая конструкция в языке, но опытные программисты на практическом примере покажут вам преимущества того или иного способа. Важно иметь практику, и понимание, что для чего предназначено. | |
| |
В качестве самостоятельного задания, которое //потребует почитать// документацию, рекомендуется составить две программы с помощью ''IF THEN ELSE'' и ''CASE'', и сравнить быстродействие на 10 ветках исполнения ((подсказка: Time)). | |
| |