Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ещё одна методичка по ЛО.doc
Скачиваний:
18
Добавлен:
23.03.2016
Размер:
433.15 Кб
Скачать

3.2. Выражения и операторы действия

Простейшие средства описания действий, которые вычисляют единственное значение, не изменяя состояния вычислений, - выражения.

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

Порядок вычисления выражений следующий:

1) вычисляются подвыражения, заключенные в скобки;

2) затем выполняются операции с наибольшим приоритетом; обычно используются следующие уровни приоритетов (в порядке убывания):

- возведение в степень **

- мультипликативные операции *, /, mod

- унарные операции +, -, abs, not

- аддитивные операции +,-

- операции сравнения =, /=, <, >, <=, >=

- логические операции and, оr;

3) операции с одинаковым приоритетом выполняются слева направо.

─────────

П р и м е р 3.3. Выражение

-Y**Z<4.0+0.5*(X+W) and МОКРЫЙ

эквивалентно следующему выражению:

((-(Y**Z)) < (4.0+(0.5*(Х+W)))) and МОКРЫЙ

──────────

В различных языках программирования правила вычисления выражений могут несколько отличаться от этих. Отметим одну особенность вычисления выражений с логическими операциями. Если первый операнд в выражении (А and В) ложен, то и значение всего выражения ложно независимо от значения второго операнда. Аналогично, если первый операнд в выражении (А оr В) истинен, то и значение всего выражения истинно независимо от значения втором операнда. Следовательно, вычислять значение втором операнда в этих случаях не надо. Обычно так и делается. Однако в языках, в которых при вычислении выражений допускается изменение значений переменных (например, глобальных переменных при вычислении функций), это может привести к неправильному выполнению программы. В остальных языках (которые мы и рассматриваем) такой порядок вычислений не может привести к логическим ошибкам. Более того, он позволяет в некоторых случаях избежать возникновения исключений. Например, при вычислении логическом выражения в программе

subtype ИНДЕКС is integer range 1..10;

type ВЕКТОР is array(ИНДЕКС) оf real;

Х: ВЕКТОР;

I: integer;

. . .

I:=11;

if (I<=10) and (Х(I)/=1.0) then

. . .

возникло бы исключение, если бы второй операнд выражения вычислялся.

Хотя нет ограничений на сложность выражений, однако выражения, содержащие более 5 - 7 операндов, трудны для чтения и понимания и поэтому, как правило, не используются.

 Операторы действия  - это средства языка, позволяющие изменять в процессе выполнения программы состояние вычислений. Самый простой оператор действия -   оператор присваивания :

Х:=Е;

где Х - переменная или другой допустимый объект данных,

Е - выражение соответствующем типа.

Правило вывода для оператора присваивания:

{PEX} Х:=Е; {Р}

Здесь утверждается, что если Р истинно для подстановки Е вместо Х перед выполнением присваивания, то Р должно быть истинно после присваивания переменной Х ее нового значения Е. Рассмотрим несколько примеров:

1) {true} Х:=5; {Х=5} : здесь Р=(Х=5),

x

Р5 =(5=5)=(true);

2) {false} X:=5; {X<>5} : здесь Р=(Х<>5),

x

Р5 =(5<>5)=(falsе);

3) {Х<О} Х:=Х+1; {Х<1} : здесь Р=(Х<1),

x

Рx+1 =(Х+1<1)=(Х<0)

4) {Х**8=10} Х:=Х*Х; {Х**4=10} : здесь Р=(Х>~4=10),

x

Рx*x =((Х*Х)**4=10)=(Х**8=10);

5) {Е=с} Х:=Е; {Х=с} :выполнение Х:=Е даст зна-

чение с в Х, если значение

выражения Е перед выполне-

нием присваивания было с;

x

6) {У=с} Х:=Е; {Y=с} : здесь РE =Р .

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

П р и м е р 3. 4. Используя правило вывода для оператора присваивания, докажем корректность следующей программы:

--{x=Х & y=Y}

t:=х;

x:=y;

y:=t;

--{х=Y & y=Х}

То есть надо доказать, что

y x t

(((x=Y & y=X)t)y)x =(x=X & y=Y).

Очевидны следующие равенства:

y x t    x t

(((x=Y & y=X)t)y)x =((x=Y & t=X)y)x =

t

= (y=Y & t=X)x = (y=Y & х=Х),

что и требовалось доказать.

_______________________

Для присваивания значений переменным используется также оператор кратного присваивания:

Х1, Х2,..., Хn :=Е1,Е2,...,Еn;

где Х1 - различные переменные или другие допустимые объекты данных, а Еn - выражения соответствующих типов.

Правило вывода:

X1,X2,...,Xn

{PE1,E2,...,En } Х1, Х2,..., Хn :=Е1,Е2,...,Еn

Это правило однозначно задает порядок выполнения оператора: сначала вычисляются в любом порядке все выражения; пусть они дают значения v1,v2,...,vn; затем Х1

присваивается значение v1,Х2 – v2,...,Хn – vn .

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

______________________

П р и м е р 3. 5. Спецификации для оператора кратного присваивания:

1) перестановка значений двух переменных может быть выполнена одним оператором

х, у:=у, х;

Действительно,

x,y

(x=X & y=Y)y,x= (x=Y & y=X);

2) спецификация

i-1   i-1

{i>=0 & S= B(j)} S , i:=S+B(i), i+1;{i>0 & S=B(j),

j=0  j=0

истинна, так как

 i-1  S,i

(i>0 & S= B(j))S+B(i),i+1 =

 j=0

 i    i-1

=(i+1>0 & S+B(i)=  B(j))=(i>=0 & S=B(j));

  j=0  j=0

3) {с=О} х,y:=х-y, y-х; {х+y=с}, так как

x,y

(х+y=с)x-y,y-x = (х-y+y-x=c) = (с=0) ;

4) {с=2*х} х,y:=х-y, х+y; {х+y=c}, так как

x,y

(х+y=с)x-y,x+y = (х-y+х+y=с) = (с=2*х) .

__________________

Оператор кратного присваивания позволяет еще раз показать нетривиальность правип вывода. Действитсньно, если описать правило вывода для оператора кратегории присваивания следующим образом:

X1 X2  Xn

{(... РE1)E2...)En}Х1,Х2,...,Хn:=Е1,Е2,...,Еn; {Р},

то смысл его будет совершенно иным. При таком описании семантики оператор

х, у:=у, х;

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

x  y  y

((х=Y & y=X)y )x  = (y=Y & y=X)x =

 (x=X), если X=Y,

=(x=Y & x=X) =  

 ( false, если X<>Y.

Это означает, что разные значения переменных х и у могут быть только в случае, когда предусловие есть false, т.е. в случае невозможного состояния вычислений. Поэтому значения х и у после выполнения оператора х, у:=у, х;

будут одинаковы и равны исходному значению переменной х:

{x=Х} х,y:=y,х; { х=Х & y=Х}

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

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

{Р}; {Р}

Этот оператор не изменяет состояния вычислений и играет вспомогательную роль.

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

Правило вывода:

{false} stop; {P}

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