понедельник, 28 марта 2011 г.

Выражения

Перевод из справочной системы Delphi

Выражения

Выражение – это конструкция, которая возвращает значение. Далее в таблице приведены примеры выражений Delphi:


X  //Переменная
@X  //Адрес переменной X 
15  //Целочисленная константа
InterestRate  //Переменная
Calc(X, Y)  //Вызов функции 
X * Y  //Произведение X и Y 
Z / (1 - Z)  //Частное Z и (1 - Z) 
X = 1.5  //Булевское значение
C in Range1  //Булевское значение
not Done  //"инвертированное" булевское значение
['a', 'b', 'c']  //Множество
Char(48)  //Преобразование значения (типа)

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


Операторы

Операторы действуют как предопределенные функции и являются частью языка Delphi. Например, выражение (X + Y) состоит из переменных X и Y (операндов) и оператора +. Когда X и Y являются целыми или вещественными числами, выражение(X + Y) возвращает их сумму. Далее приведены операторы, используемые в языке Delphi: @, not, ^, *, /, div, mod, and, shl, shr, as, +, -, or, xor, =, >, < , <>, <=, >=, in, is.

Операторы @, not и ^ являются унарными (принимают один операнд). Все остальные операторы являются бинарными (принимают два операнда). Исключение составляют операторы + и -, которые могут функционировать как унарные и как бинарные. Унарный оператор всегда предшествует своему операнду (например, -B). Исключение составляет оператор ^, который следует за своим операндом (например, P^). Бинарный оператор помещается между своими операндами (например, A = 7).

Некоторые операторы действуют по разному в зависимости от типа данных операндов. Например, not выполняет побитовое отрицание на целочисленном операнде и логическое отрицание на булевском операнде. Операторы, сгруппированные по категориям, привдены ниже. За исключением ^, is, in, все операторы могут принимать операнды типа Variant.


Арифметические операторы

Арифметические операторы, принимающие целочисленные и вещественные операнды, - это +, -, *, /, div и mod.


Бинарные арифметические операторы:
ОператорОперацияТип операндаТип результатаПример
+Сложениеinteger, realInteger, realX + Y
-Вычитаниеinteger, realInteger, realResult -1
*Умножениеinteger, realInteger, realP * InterestRat
/Вещественное делениеinteger, realrealX / 2
divЦелочисленное делениеintegerintegerTotal div UnitSize
modОстатокintegerintegerY mod 6

Унарные арифметические операторы:
ОператорОперацияТип операндаТип результатаПример
+Сохранение знакаinteger, realinteger, real+7
-Смена знакаinteger, realinteger, real-X

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

  • Значение x / y имеет тип Extended, вне зависимости от типов x и y. Для остальных арифметических операторов – результат будет иметь тип Extended, в том случае, когда хотя бы один из операндов является вещественным. Иначе, если хотя бы один из операндов имеет тип Int64, результат будет иметь тип Int64, иначе результат будет иметь тип Integer. Если тип операнда является подмножеством типа Integer, он обрабатывается как тип Integer.
  • Значение x div y – это результат деления x / y, округленное к ближайшему целому числу в направлении ноля.
  • Оператор mod возвращает остаток, полученный при делении его операндов. Другими словами x mod y = x - (x div y) * y.
  • При вычислении выражения вида x / y, x div y или x mod y, в том случае, когда y=0, возникает ошибка выполения программы.


Булевские операторы

Булевские операторы not, and, or и xor принимают операторы любого булевского типа и возвращают значение типа Boolean.

ОператорОперацияТип операндаТип результатаПример
notОтрицаниеBooleanBooleannot (C in MySet)
andКонъюнкцияBooleanBooleanDone and (Total >0)
orДизъюнкцияBooleanBooleanA or B
xorИсключающая дизъюнкцияBooleanBooleanA xor B

Эти операторы обрабатываются стандартными правилами булевской логики. Например, выражение вида x and y возвращает True, если x и y имеют значение True.


Полное или частичное вычисление булевских значений

Компилятор поддерживает два режима вычисления для операторов and и or: полное вычисление и частичное. Полное вычисление подразумевает, что каждый элемент выражения вычисляется даже в том случае, когда результат вычисления всего выражения уже известен. Частичное вычисление подразумевает четкое вычисление выражения слева-направо, которое останавливается, когда становится известен результат вычисления всего выражения. Например, если выражение (A and B) рассчитывается по методу частичного вычисления, то, когда A имеет значение False, компилятор не станет вычислять B, поскольку результат выражения (False) известен уже на момент вычисления A.

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

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

while (I <= Length(S)) and (S[I] <> ',') do
begin
 ...
 Inc(I);
end;

В случае, когда S не содержит запятых, в последней итерации I увеличивается до значения, которое больше длины S. При вычислении условия в инструкции while полное вычисление выражения приведет к попытке прочитать значение S[I], при которой возникнет ошибка выполнения. При частичном вычислении вторая часть выражения в инструкции while (S[I] <> ',') не будет вычисляться после того, как первая часть условия не будет выполнена.

Для управления режимами вычисления используйте директиву компилятора $B. По умолчанию состояние переключателя {$B}, что включает частичное вычисление выражений. Для локального включения режима полного вычисления выражений, добавьте директиву {$B+} в ваш программный код. Вы можете установить режим полного вычисления выражений для всего проекта, установив переключатель Complete Boolean Evaluation в диалоговом окне Compiler Options (потребуется перекомпиляция всех модулей).

Замечание: Если какой либо из операндов имеет тип variant, компилятор всегда выполняет полное вычисление выражений (даже в состоянии {$B}).



Логические (побитовые) операторы

Следующие логические операторы выполняют побитовые манипуляции в операндах типа integer. Например, если значение, хранящееся в переменной X (в двоичном представлении) – 001101, а значение, хранящееся в переменной Y – 100001, инструкция:

Z := X or Y;

присваивает значение 101101 переменной Z.

ОператорОперацияТип операндаТип результатаПример
notПобитовое отрицаниеintegerintegernot X
andПобитовое andintegerintegerX and Y
orПобитовое orintegerintegerX or Y
xorПобитовое xorintegerintegerX xor Y
shlПобитовый сдвиг влевоintegerintegerX shl 2
shrПобитовый сдвиг вправоintegerintegerY shr I

К побитовым операторам применяются следующие правила:

  • Результат операции not совпадает с типом операнда.
  • Если операнды в операциях and, or или xor имеют тип integer, результатом вычисления выражения будет значение предопределенного целочисленного типа с наименьшим необходимым диапазоном, включающим все возможные значения обоих типов.
  • Операции x shl y и x shr y смещают значение x влево или вправо на y бит, что эквивалентно (если x – целое без знака) умножению или делению x на 2^y. Результат операции будет иметь тот же тип, что и x. Например, если N содержит значение 01101 (десятичное 13), тогда N sh 1 вернет 11010 (десятичное 26). Необходимо учесть, что значение y интерпретируется по остатку от деления на размер типа переменной x. То есть, например, если x имеет тип integer, выражение x shl 40 интерпретируется как x shl 8 поскольку тип integer является 32 битным и 40 mod 32 = 8.


Строковые операторы

Операторы отношения =, <>, <, >, <= и >= принимают строковые операнды.

Оператор "+" выполняет сцепку двух строк.

ОператорОперацияТип операндаТип результатаПример
+СцепкаString, packed string, characterstringS + '.'

К строковым операторам применяются следующие правила:

  • Операнды для оператора + могут быть строками, упакованными строками (упакованными массивами типа Char) или символами. Однако, в том случае, когда один из операндов имеет тип WideChar, другой операнд должен иметь тип UnicodeString, AnsiString или WideString.
  • Результат операции + совместим с любым строковым типом, но, если оба операнда имеют тип ShortString или Char, а результат их сцепки имеет длину более 255 символов, результат обрезается до 255 символов.


Операторы указателей

Операторы отношения <, >, <= и >= могут принимать операнды типов PAnsiChar и PWideChar. Следующие операторы могут также принимать указатели как операнды.

ОператорОперацияТип операндаТип результатаПример
+Сложение указателейcharacter pointer, integercharacter pointerP + I
-Вычитание указателейcharacter pointer, integercharacter pointer, integerP - Q
^Доступ по адресу (разыменование)pointerbase type of pointerP^
=РавенствоpointerBooleanP = Q
<>НеравенствоpointerBooleanP <>Q

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

P = Q возвращает значение True только в том случае, когда P и Q указывают на один и тот же адрес. Во всех остальных случаях P <> Q возвращает значение True.

Вы может использовать операторы + и - для увеличения и уменьшения смещения символьных указателей. Вы также можете использовать оператор "–" для расчета разницы между смещениями двух символьных указателей. Кроме того, действуют следующие правила:

  • Если I имеет тип integer, а P – это символьный указатель, то выражение P + I добавляет I к адресу, на который ссылается P; то есть оно возвращает указатель на адрес I-того символа после P. (Выражение I + P эквивалентно P + I.). P - I вычитает I из адреса, на который ссылается P; то есть оно возвращает указатель на адрес I-того символа перед P. Это утверждение верно для указателей PAnsiChar; для указателей PWideChar выражение P + I добавляет SizeOf(WideChar) к P.
  • Если P и Q являются символьными указателями, тогда выражение P - Q вычисляет разницу между адресами, на которые ссылаются P и Q. То есть оно возвращает целое число, соответствующее количеству символов между P и Q. Выражение P + Q будет иметь неопределенное значение.


Операторы множеств

Следующие операторы принимают множества как операнды.
ОператорОперацияТип операндаТип результатаПример
+ОбъединениеsetsetSet1 + Set2
-РазницаsetsetS - T
*ПересечениеsetsetS * T
<=ПодмножествоsetBooleanQ <= MySet
>=РасширениеsetBooleanS1 >= S2
=РавенствоsetBooleanS2 = MySet
<>НеравенствоsetBooleanMySet <> S1
inЧленствоordinal, setBooleanA in Set1

К операторам +, - * применяются следующие правила:

  • Порядковое O входит в X + Y только в том случае, когда O входит в X илиY (или в оба). O входит в состав X - Y только в том случае, когда O входит в X, но не в Y. O входит в X * Y только в том случае, когда O входит и в X и в Y.
  • Результат операций +, - или * имеет тип set of A..B, где A – это наименьшее порядковое значение в результирующем множестве, а B - наибольшее.

К операторам <=, >=, =, <> и in применяются следующие правила:

  • X <= Y возвращает значение True только в том случае, когда каждый член X является членом Y. Z >= W эквивалентно W <= Z. U = V возвращает значение True только в том случае, когда U и V содержат одинаковые члены; в противном случае, значение True будет возвращать выражение U <> V.
  • Для порядкового O и множества S, выражение O in S возвращает True только в том случае, когда O является членом S.


Операторы отношения

Операторы отношения служат для сравнения двух операндов. Операторы =, <>, <=, и >= так же применимы к множествам.

ОператорОперацияТип операндаТип результатаПример
=Равенствоsimple, class, class reference, interface, string, packed stringBooleanI = Max
<>Неравенствоsimple, class, class reference, interface, string, packed stringBooleanX <> Y
<Меньшеsimple, string, packed string, PCharBooleanX < Y
>Большеsimple, string, packed string, PCharBooleanLen > 0
<=Меньше либо равноsimple, string, packed string, PCharBooleanCnt <= I
>=Больше либо равноsimple, string, packed string, PCharBooleanI >= 1

Для большей части простых типов сравнение несложно. Например, I = J возвращает значение True в том случае, когда I и J имеют одинаковое значение, в противном же случае значение True возвращает выражение I <> J. К операторам отношения применяются следующие правила:

  • Операнды должны иметь совместимые типы. Исключение составляют значения целочисленных и вещественных типов (поскольку их можно сравнивать).
  • Строк сравниваются в соответствии с порядковыми значениями, представляющими символы, которые входят в строки. Символьные типы обрабатываются как строки с длиной, равной 1.
  • Две упакованные строки для того, чтобы их можно было сравнивать, должны иметь одинаковое количество компонентов. Когда упакованная строка с n компонентов сравнивается со строкой, упакованная строка обрабатывается как строка длины n.

Используйте операторы <, >, <= и >= для сравнения операндов типа PAnsiChar (и PWideChar) только если два указателя ссылаются на символы внутри одного массива.

Операторы = и <> могут принимать в качестве операндов объектные переменные и переменные типа ссылки на класс. С объектными операндами = и <> вычисляются по правилам указателей: C = D возвращает значение True в том случае, когда C и D ссылаются на один и тот же экземпляр класса, в противном случае, значение True возвращает выражение C <> D. С операндами типа ссылки на класс, выражение C = D возвращает значение True в том случае, когда C и D обозначают один и тот же класс, в противном случае значение True возвращает выражение C <> D. Такие операции не выполняют сравнения данных, содержащихся в экземплярах классов.



Операторы классов и интерфейсов

Операторы as и is принимают классы и экземпляры классов в качестве операндов. Оператор as также работает и с интерфейсами Операторы отношения = и <> могут оперировать с классами.


Оператор @

Оператор @ возвращает адрес переменной или функции, процедуры или метода. То есть оператор @ создает указатель на свой операнд.

К оператору @ применяются следующие правила:

  • Если X – переменная, то @X возвращает адрес X. (Особые правила действуют, когда X – переменная процедурного типа). Если включенная по умолчанию директива компилятора {$T} включена, – тип значения, возвращаемого @X – Pointer. В состоянии {$T+}, @X возвращает значение типа ^T, где T – это тип X (это различие важно для совместимости по присваиванию).
  • Если F – это подпрограмма (функция или процедура), выражение @F возвращает точку входа в F. Выражение @F всегда возвращает значение типа Pointer.
Когда @ применяется к методу, определенному в классе, идентификатор метода должен иметь спецификатор – имя класса. Например:

@TMyClass.DoSomething //указывает на  метод 
//DoSomething класса TMyClass. 
Замечание: при использовании оператора @ невозможно получить адрес метода интерфейса, поскольку этот адрес неизвестен на момент компиляции и не может быть получен в процессе выполнения программы.


Приоритет операторов

В сложных выражениях правила приоритета определяют порядок выполнения операций.

ОператорыПриоритет
@, not Первый (наивысший)
*, /, div, mod, and, shl, shr, asВторой
+, -, or, xorТретий
=, <>, <, >, <=, >=, in, isЧетвертый (самый низкий)

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

То есть, выражение:

X + Y * Z

умножает Y на Z, затем прибавляет X к полученному произведению; * выполняется в первую очередь, поскольку имеет более высокий приоритет, чем +., но:

X - Y + Z

сначала вычитает Y из X, затем добавляет Z к результату; - и + имеют одинаковый приоритет и вычисляются слева направо.

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

(X + Y) * Z

умножает сумму X и Y на Z.

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

X = Y or X = Z

Интерпретация этого выражения очевидна:

(X = Y) or (X = Z)

Однако без скобок компилятор следует правилу приоритета операторов и разбирает выражение следующим образом:

(X = (Y or X)) = Z

что приводит к ошибке компиляции в тех случаях, когда Z не является значением типа Boolean.

Скобки часто делают код более простым для чтения и написания. Даже в тех случаях, когда они, строго говоря, являются избыточными. Например, в выражении:

X + (Y * Z)

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



Вызовы функций

Поскольку функции возвращают значение вызовы функций являются выражениями. Например, если вы определили функцию с именем Calc, которая принимает два целочисленных параметра и возвращает целочисленный результат, то вызов функции Calc(24,47) – это целочисленное выражение. Если I и J – это целочисленные переменные, то I + Calc(J,8) также будет целочисленным выражением. Примеры вызовов функций приведены ниже:

Sum(A, 63)
Maximum(147, J)
Sin(X + Y)
Eof(F)
Volume(Radius, Height)
GetValue
TSomeObject.SomeMethod(I,J);


Конструкторы множеств

Конструкторы множеств определяют значения типа множество. Например:

[5, 6, 7, 8]

определяет множество, членами которого являются 5, 6, 7 и 8. Конструктор множества вида:

[ 5..8 ]

определяет множество с этими же членами.

Синтаксис конструктора множества:

[ item1, ..., itemn ] 

где item – может быть выражением, определяющим порядковое значение из базового типа множества или двумя такими выражениями, разделенных точками (..). Когда item имеет форму x..y, он обозначает включение всех порядковых значений в диапазоне от x до y (включая y). В том случае, когда x больше y, множество [x..y] будет пустым. Конструктор множества [ ] также определяет пустое множество, а конструктор вида [x] определяет множество с единственным членом x.

Примеры конструкторов множеств:

[red, green, MyColor]
[1, 5, 10..K mod 12, 23]
['A'..'Z', 'a'..'z', Chr(Digit + 48)]


Индексы

Для строк, массивов, свойств-массивов и указателей на строки или массивы возможен доступ к элементу массива или строки по его индексу. Например, если FileName – это строковая переменная, выражение FileName[3] возвращает третий символ в строке, хранящейся в переменной FileName, а выражение FileName[I + 1] возвращает сивол, следующий сразу за символом с индексом I.



Преобразование типов

Иногда необходимо обработать выражение таким образом, как будто оно имеет тип, отличный от исходного. Преобразование типа позволяет вам сделать это фактически временно изменив тип выражения. Например, Integer('A') преобразовывает символ "A" в число.

Синтаксис преобразования типа:

typeIdentifier(expression)

Если expression – это переменная, результат действия называется преобразованием типа переменной, в противном случае – преобразованием типа значения. Несмотря на то, что синтаксис этих операций одинаков, существуют различия в правилах преобразования типов.



Преобразование типа значения

При преобразовании типа значения, идентификатор типа и преобразуемое выражение должны относиться к порядковым или указательным типам. Например:

Integer('A')
Char(48)
Boolean(0)
Color(2)
Longint(@Buffer)

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

Инструкция:

I := Integer('A');

присваивает значение Integer('A') (равное 65) переменной I.

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



Преобразование типа переменной

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

Char(I)
Boolean(Count)
TSomeDefinedType(MyVariable)
Преобразование типа переменной может находиться в любой части инструкции присваивания:
var MyChar: char;
  ...
  Shortint(MyChar) := 122; //присваивает значение "z" 
// (ASCII 122)переменной MyChar. 
Вы можете преобразовывать переменные к процедурному типу. Например для объявления:
type Func = function(X: Integer): Integer;
var
  F: Func;
  P: Pointer;
  N: Integer;
Вы можете выполнить следующие инструкции присваивания:
F := Func(P);     { Присваивает переменной F 
значение процедурного типа, хранящееся в P }
Func(P) := F;     { Присваивает переменной P 
значение процедурного типа, хранящееся в F}
@F := P;          { Присваивает переменной F 
значение указательного типа, хранящееся в P }
P := @F;          { Присваивает переменной P 
значение указательного типа, хранящееся в F }
N := F(N);        { вызов функции с использованием F }
N := Func(P)(N);  { вызов функции с использованием P }
При преобразовании типа переменной могут указываться спецификаторы:
type
  TByteRec = record
     Lo, Hi: Byte;
  end;

  TWordRec = record
     Low, High: Word;
  end;

var
  B: Byte;
  W: Word;
  L: Longint;
  P: Pointer;

begin
  W := $1234;
  B := TByteRec(W).Lo;
  TByteRec(W).Hi := 0;
  L := $1234567;
  W := TWordRec(L).Low;
  B := TByteRec(TWordRec(L).Low).Hi;
  B := PByte(L)^;
end;

В этом примере TByteRec используется для доступа к старшим и младшим байтам слова (word), а TWordRec - для доступа к старшим и младшим словам длинного целого (long integer). Вы можете вызывать стандартные функции Lo и Hi для аналогичных задач, но преобразование типа имеет то преимущество, что может быть использовано в левой части инструкции присваивания.

1 комментарий:

  1. Строки UTF8string умеют запоминать русский текст. Но при попытке обратиться к конкретному символу получается пустота. Видимо, type(s[i])=char, поэтому русские символы туда "не умещаются". Что делать?

    ОтветитьУдалить