9.3. Логические вычисления и операции отношения

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

В Турбо Паскале введены четыре логических операции (табл. 9.4, где L1 и L2 — логические константы, переменные или выражения, равные True или False).

Таблица 9.4

Название

Запись

Результат операции

not

Логическое 'НЕ' (отрицание)

not A1

Логическое значение, противоположное значению L1

and

Логическое 'И' (конъюнкция)

L1 и L2

Логическое значение True, если L1 и L2 равны True, и False во всех остальных случаях

- 172 -

or

Логическое 'ИЛИ' (дизъюнкция)

L1 or L2

Логическое значение True, если хотя бы одно из значений L1 или L2 равно True, и False, если L1 и L2 равны False

xor

Логическое исключающее 'ИЛИ'

L1 xor L2

Логическое значение True, если значения L1 и L2 различны, и False, если они равны

Результат операции всегда имеет тип Boolean и может иметь только одно из двух значений : True (истинно) или False (ложно). Логические операции имеют различные приоритеты (см. табл. 9.1), и в случае, если L1 и L2 сами являются логическими выражениями, лучше расставлять скобки, явно задавая порядок логических вычислений. Так, например, запись

not L1 and L2

будет воспринята компилятором как

(not L1) and L2,

в то время как, может быть, нужно было записать

not (L1 and L2).

С порядком логических вычислений тесно связана особая возможность Турбо Паскаля — поддержка двух различных моделей генерации кода для операций or и and — полное вычисление и вычисление по короткой схеме (частичное вычисление). При полном вычислении подразумевается, что каждый член логического выражения, построенного с помощью операций or и and, всегда будет вычисляться, даже если результат всего выражения уже известен. Вычисление по короткой схеме прекращается, как только результат всего выражения становится очевиден. Во многих случаях эта модель более удобна, поскольку она обеспечивает минимальное время выполнения и, как правило, минимальный объем кода. Вычисление по короткой схеме делает возможными такие конструкции, которые в противном случае были бы недопустимы, например:

if ( J <> 0 ) and ( ( 2/J ) > х ) then ... ;

while ( i <= Length(S) ) and ( S[i] <> ' ' ) do ... ;

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

- 173 -

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

if LogFunc(x) and LogFunc2(x) then ... ;

где LogFuncl и LogFunc2 — кроме анализа значения x, производят модификацию данных или выдачу сообщений.

Схема вычисления задается с помощью ключа компилятора $B. Обычно значением по умолчанию является состояние {$B-} (установленное в меню настройки компилятора Options/Compiler). В этом случае генерируется код с вычислением по короткой схеме. В случае директивы {$B+} генерируется код с полным вычислением.

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

Типы операндов и результаты операций отношения (сравнения) приведены в табл. 9.5. Сравнивать можно совместимые простые значения, указатели, символы, строки. Подобие операций сравнения существует и для множеств. Результат операции отношения — всегда логическое значение True или False.

Таблица 9.5

Название

Запись

Результат операции

=

Равно

X1 = X2

Логическое значение True, значения X1 и X2 равны, и False в противоположном случае

<>

Не равно

X1 <> X2

Логическое значение True, если значения X1 и X2 различны, и False в противном случае

<

Меньше чем

X1 < X2

Логическое значение True, если значение X1 меньше значения X2 в смысле, определенном для типа этих значений, и False в противном случае

- 174 -

>

Больше чем

X1 > X2

Логическое значение True, если значение X1 больше значения X2 в смысле, определенном для типа этих значений, и False в противном случае

<=

Меньше или равно

X1<=X2

Логическое значение True, если значение X1 не больше значения X2 в смысле, определенном для типа этих значений, и False в противном случае

>=

Больше или равно

X1>=X2

Логическое значение True, если значение X1 не меньше значения X2 в смысле, определенном для типа этих значений, и False в противном случае

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

Обычно логические выражения встречаются в программе либо в операторах управления с условием (IF, WHILE, REPEAT...UNTIL и др.), либо в присваивании значений в логические переменные. С помощью приведения типов можно вставлять логические выражения прямо в числовые. Например, если надо ограничить величину X, например, значением 15, то обычно строится конструкция

if X>15 then X:=X-15;

Но если учесть, что False хранится как байт со значением 0, a True — как байт со значением 1, то можно обойтись без if:

X := X - 15 * Byte( X>15 );

Здесь 15 вычтется только, если (X>15)=True, что значит 1. Иначе же, вычтется 15*0. Можно и из байтов делать логические значения приведением к типу Boolean. Но тут надо помнить, что

Boolean( X ) = False, если X=0,

Boolean( X ) = True , если X<>0.

Значение X должно иметь тип Byte. Как видно, ненулевое значение всегда «истинно».

- 175 -

Еще одно замечание: при сравнении вещественных значений нельзя быть уверенным в его корректности. Например, выражения X и ( 2.23 * X / 2.23 ) формально тождественны, но из-за ошибок округления в вещественном типе будут различаться. Еще большие проблемы возникают при работе с расширенным набором вещественных типов, разрешенных при наличии математического сопроцессора 80X87. Советуем посмотреть разд. 9.5, посвященный использованию сопроцессора, где рассказывается, как бороться с подобными трудностями.