Теоретическое программирование – математическая дисциплина, изучающая синтаксические и семантические свойства программ, процессы составления, преобразования и выполнения. ПРОГРАММИРОВАНИЕ искусство ремесло наука Программирование как наука показывает как можно достичь множества целей, применяя строго определенные правила построения к основным конструктивным элементам. Пример: х делим на y, r – остаток от деления. r:= x; q:=0; while r>y do begin r:=r-y; q:=q+1; end; write (‘yq+r’,yqr) Организуем программу по-другому: {y>0} r:=x; q:=0; while r>y do begin r:=r-y; q:=q+1; end; {x:= yq+r} Однако, для того чтобы программа выполнялась для r y, необходимо откорректировать данную программу, добавив в нее условия: {y>0 and x 0} r:=x; q:=0; while r>y do begin r:=r-y; q:=q+1; end; {x:= yq+r and 0 ry} СПЕЦИФИКАЦИЯ ПРОГРАММ Спецификация программ – выражение на определенном языке, возможно естественном, который точно описывает что должно быть совершено в результате выполнения программы. Для составления спецификации программ целесообразно использовать язык предикатов. Спецификация программы на языке предикатов выглядит следующим образом: {Q} S {R}, где Q – предусловие, S –программа, R – постусловие . Если выполнение S началось в состоянии, удовлетворяющем Q, то имеется гарантия, что оно завершится в R. Пример1: записать в z произведение a и b, предполагается, что значения a и b – положительные величины. a). {a 0 and b 0} S {z = a * b} b). Известно начальное значение переменных: {a 0 and b 0 and a = A and b = B} S {z = a * b and a = A and b = B } assert(a 0) – если программа “вылетает”, то выдается номер строчки и условие. Пример2: сортировка. a). {n 0} S { i: 0 i < n-1: b[i] b[i+1] } b). {n 0 and b = B} S { i: 0 i < n-1: b[i] b[i+1] and b = перем.(B) } где b является перемещением от B. Пример3:обмен значениями переменных. {x = X y = Y} S {x = Y y = X} Пример4:найти максимальное значение переменных в массиве B, который изменяется от 0 до n-1: {n>0} S {(i: 0 i < n: b[i] x) ( i: : 0 i < n: x = b[i]) } Пример5:придать x абсолютное значение x. {x = X} S {(X 0 x = -X ) (X 0 x = X)} или {(x 0) ( x = X x = -X)} Пример6:придать каждому значению массива b[0:n-1] значение суммы элементов этого массива. {n 0 b = B} S { i:0in:b[i] = j:0jn:B[j]} Пример7:в массиве a[0:n-1] отсортирован список преподавателей Политеха, в b[0:m-1] отсортирован список, получающих пособие по безработице. Найти первого “жулика”, который преподает и получает пособие. {n0 m>0} S {0 p<n 0q<m a[p] = b[q] (i,j:0 i p 0 j q:a[i] = b[j]) = 1} Задания для самостоятельной работы: 1).Найти место первого максимального значения в массиве. 2).Найти n-ое число Фиббоначи. ПРЕОБРАЗОВАТЕЛЬ ПРЕДИКАТОВ WP. Wp(S,R) – предикат, который определяет множество всех состояний, для которого выполнение команды S закончится через некоторое конечное время в состоянии, удовлетворяющем R. Предусловие является слабейшим для S по отношению к R, так как оно определяет множество всех состояний, таких что выполнение , начавшееся в любом из них заканчивается при истинном R. Пример1: wp(“i:=i+1”,i1)=i0 Пример2: S: if x y then z:=x else z:=y R: z = max(x,y) wp(S,R) = T ЧАСТИЧНАЯ И ПОЛНАЯ КОРРЕКТНОСТЬ. {Q} S {R}- полная корректность В частичной корректности фразы “через конечное время” нет. Q {S} R Если выполнение команды S начинается в состоянии команды, удовлетворяющем Q и если оно заканчиваеться, то конечное состояние бедет удовлетворять R. Пример: T {while(1){}} - получили тавтологию, так как при частичной коррекции нас не волнует закончится цикл или нет. {T} while(1){} {T} - ложь, так как в полной корректности программа закончится через конечное время, а, следовательно, противоречие получено. НЕКОТОРЫЕ СВОЙСТВА WP. 1.) Закон “исключенного чуда”. wp(S,F) = F 2.) Дистрибутивность конъюнкции. wp(S,Q) wp(S,R) = wp(S,Q R) 3.) Закон монотонности. Если из Q следует R, то из wp(S,Q) следует wp(S,R). 4.) Дистрибутивность дизъюнкции. wp(S,Q)wp(S,R)wp(S,QR) КОМАНДЫ SKIP И ABORT . wp(skip,R)=R wp(abort,R)=F-аварийное завершение КОМПОЗИЦИЯ КОМАНД. Последовательное соединение – один из способов составления больших программных сегментов из меньших. Определим последовательное соединение в термиах wp: wp(s1;s2,R)=wp(s1,wp(s2,R)) – то есть постусловие выполнения команды s1 должно быть предусловием того, что выполнение s2 даст R. wp(“skip;skip”,R)=wp(“skip”,wp(“skip”,R))=wp(“skip”,R)=R Пример1: wp(“i:= i +1; j:= i -1”, i*j=0) = wp(“i:= i +1”,wp(“j:= i -1”, i*j=0)) = wp(“i:= i +1”, i *( i -1)=0) = ((i+1)( i +1-1)=0) = ((i +1)* i =0)=( i =0, i =-1); Пример2: wp(“i:= i +1; j:=j-1”, i *j=0) = wp(“i:= i +1”,wp(“j:=j-1”, i*j=0)) = wp(“i:= i +1”, i *( j -1)=0) = ((i+1)( j-1)=0) =( i =-1, j =1); Пример3: wp(“x:= 2+y; y:=x*y”, y+x=0) = wp(“x:= 2+y”,wp(“y:=x*y”, y+x=0)) = wp(“x:= 2+y”, x*y+x=0) = ((2+y)*y+2+y)=0) =( y =-1, y =-2); КОМАНДА ПРИСВАИВАНИЯ. wp(“x:=e”,R)=domain(e) cand R wp(“x:=e”,R)=R wp(“x:=5”, x=5)=(x=5)=(5=5)=T wp(“x:=5”, x5)=(x=5)=(5=5)=T Выполнение присваивания может изменить лишь ту переменную, которая стоит в левой части: wp(“t:=x; x:=y; y:=t”,x=X y=Y) = wp(“t:=x; x:=y; wp(“y:=t”,x=X y=Y))=wp(“t:=x; x:=y”; x=X t=Y) = wp(“t:=x”; y=X t=Y) = (y=X x=Y) Задания для самостоятельной работы: 1. 1). wp(“i:=i+1”, i>0) 2). wp(“i:=i+2;j:=j-2”, i+j=0) 3). wp(“z:=z*j; i:=i-1”, z*ji=c) 4). wp(“i:=i+1;j:=i-1”,i*j=0) 2. 1). wp(skip, i>0) wp(skip, i0) 2). wp(skip, i0) wp(skip, i0) 3). wp(skip, i>0) wp(skip, i<0) 4). wp(skip, i>0) wp(abort, i<0) 5). wp(skip, i>0) wp(abort, i>0) 3. Определить верно ли равенство wp(“s1;s2”,R) = wp(“s2;s1”,R) при следущих значениях переменных: 1). s1: i:=i+1 s2: j:=j+1 R: i>j 2). s1: i:=j+1 s2: i:=i*2 R: i=j 3). s1: j:=i s2: i:=i+1 R: i=j КРАТНЫЕ ПРИСВАИВАНИЯ ПРОСТЫХ ПЕРЕМЕННЫХ. x1,x2:=3,4 x1,x2, x3,...,xn:= e1,e2,e3,...,en e1 v1 x1 v1 e2 v2 x2 v2 . . . . . . . x := e wp(“ x := e ”, R) = domain( e ) cand Rex i domain( e i ) wp(“x,y:=y,x”,x=X y=Y) = (x=X y=Y) xy,, yx =(y=X x=Y) ПОИСК РЕШЕНИЙ С ПОМОЩЬЮ WP. Пример1: В памяти имеется массив длиной p. Левую границу изменим, а правая должна остаться на месте. Спецификация программы: {i+p=c} i,p:=m+1,x {i+p=c} wp(“i,p:=m+1,x”,i+p=c)=m+1+x=c С предусловием составим систему уравнений: i p c m 1 x c Следовательно, x=i+p-m-1 Пример2: {T} a:=a+1; b:=x(a,b) {a=b} wp(“s1;s2”,R) = wp(s1,wp(s2,R)) wp(“a:=a+1”, wp(“b:=x(a,b)”,a=b) = wp(“a:=a+1”,a=x(a,b))=(a+1=x(a+1,b)) т. е. x(a,b)=a – функция от двух аргументов возврвщает первый аргумент. ПРИСВАИВАНИЕ ЭЛЕМЕНТУ МАССИВА. b[i]:=e b:=(b; i:e)- по i-ому элементу записано e. (b;2:7)[2] = 7- во втором элементе записана цифра 7. (b;1:4)[2] = 2 – на первом месте массива цифра 4. Введем wp: wp(“b[i]:=e”,R)=wp(“b:=(b;i:e)”,R)=domain(b;i:e) cand Rb(b;i:e) где i – не должен выходить за границы массива e. Пример1: wp(“b[i]:=5”,b[i]=5)=((b;i:5)[i]=5)=(5=5)=T Пример2: wp(“b[i]:=5”,b[i]=b[j])=((b;i:5)[i]=(b,i:5)[j])=(5=(b;i:5)[j])=(i=j5=5)(ij5 =b[j])=(i=j)( ij5=b[j])=(i=j ij)(i=j5=b[j])=(i=j5=b[j]) Пример3: wp(“b[b[i]]:=i”,b[i]=i)=((b;b[i]:i)[i]=i)=(i=b[i]i=i)( ib[i]b[i]=i)=(i=b[i]) Задания для самостоятельной работы: 1. 1). wp(“z,x,y:=1,c,d”,z*xy=cd) 2). wp(“i,s:=1,b[0]”,1 i <n s = b[0]+b[1]+…+b[i-1]) 3). wp(“a,n:=0,1”,a2<n<(a+1)2n) 4). wp(“i,s:=i+1,s+b[i]”,0<i<n s= b[0]+b[1]+…+b[i-1]) 2. Найти x: 1) {T} a,b:=a+1;x {b=a+1} 2) {T} a:=a+1; b:=x {b=a+1} 3) {z+a*b=c} z,a:=z+b,x {z+a*b=c} 3. 1). wp(“b[i]:=i”,b[b[i]]=i) 2). wp(“b[i]:=5”, j:ij<n: b[i] b[j]) КОМАНДА ВЫБОРА. if x<0 then x:=-x else x:=x if x 0 x:=-x x 0 x:=x fi Общий случай: if B1 S1 B2 S2 B3 S3 B4 S4 ………… fi где Bi – охрана; Si – охраняемая команда; Введем wp: BB = B1B2B3…Bn Если BB – ложно, значит ни одна из охран не пустила. Определение: wp(IF,R)=domain(BB) cand BB cand (B1wp(S1,R) B2wp(S2,R)… Bnwp(Sn,R)) wp(IF,R)=( i:1 i n:Bi)( i:1 i n:Bi wp(Si;R)) wp(IF,R)=BBB1wp(S1,R)B2wp(S2,R) if B1S1 B2S2 fi BB=B1B2 wp(“if x 0z:=-x x 0z:=x ”,z=abs(x))=( x 0 x 0) x 0wp(“z:=-x”,z=abs(x)) x 0 wp(“z:=x”,z=abs(x))= =( x 0-x=abs(x))( x 0x=abs(x))=T Пример: посчитать число положительных элементов в массиве. if b[i] > 0 i,p:=i+1,p+1 b[i] < 0 i:=i+1 fi {i<n p=N j: 0j<i:b[j]>0} wp(IF,R)=(b[i]0) (b[i]0wp(“i,p:=i+1,p+1,R”)) (b[i]<0 wp(“i:=i+1,R”))=(b[i]0) (b[i] 0i+1 n p+1=N j:=0j<i+1:b[j]>0) (b[i] <0i+1 n p=N j:=0j<i+1:b[j]>0)= =(b[i]0) (i<n p=N j:0 j<i:b[j]>0) (i<n p=N j:0 j<i:b[j]>0)= =(b[i]0) (i<n p=N j:0 j<i:b[j]>0) Задания для самостоятельной работы: 1). wp(“if x>y x,y:=y,x xy skip fi ”,xy ) 2). if a>b a:=a-b” b>a b:=b-a fi R1: a>b b>a R2: a>0 b>0 ТЕОРЕМА О КОМАНДЕ ВЫБОРА. Рассмотрим предикат Q: 1).QBB; 2).Q Bi=wp(Si,R); то Qwp(IF,R) Пример: Поиск методом половинного деления Ищем x в массиве b[0:n-1] Q:ordered(b) 0 i k j<n xb[i:j] Постусловие: R:xb[i:j] Программа: if b[k] xi:=k b[k] xj:=k fi Проверка теоремы: 1). Из QBB, т.е. ordered(b) 0 i k j<n xb[i:j] b[k] x b[k] x = T т.е.первое условие выполнено. 2). Выполнение Si должно привести к R. 1 случай: Q b[k] x xb[k:j] wp(i:=k,xb[i:j]) 2 случай: Q b[k] x xb[k:j] wp(i:=k,xb[i:j]) Задания для самостоятельной работы: Проверить соответствие программ спецификации, используя теорему о команде выбора: 1). {0<i<n m=max(b[0: i-1])} if b[i]>m m:=b[i] b[i]m skip fi {0<i<n m=max(b[0: i])} 2). {ordered(b[0:n]) 0 i n 0 j n ij } if i>j i,j:=j,i i,j skip fi {b[i] b[j]} 3). {y>0 z+y*x=a*b} if неч(x) z,x:=z+y,x-1 чет() skip fi; y,x:=2*y, x/2 {y0 z+y*x=a*b} КОМАНДА ПОВТОРЕНИЯ. i:=0 while i<10 do i:=i+1 done Общий вид: While B do S - пока условие выполняется делать команду. do B1S1 B2S2 B3S3 ………… od т.е. в зависимости от того какая охрана пропускает происходит выполнение соответствующей команды. Введем команду повторения через слабейшее предусловие: H0(R)=BB R, где BB=B1B2B3… Hk(R)=7 H0(R)wp(IF,Hk-1(R)), k>0 wp(DO,R)=k:0 k: Hk(R) Сформулируем теорему для неслабейшего условия: Программа ищет сумму элементов массива. 1.Начальная инициализация переменных. i,s:=1,b[0] do i<11i,s:=i+1,s+b[i] od В цикле инициализация переменных присутствует почти всегда. 2.Рассмотрим некоторый предикат P: {R:S=k:0 k 11:b[k]} P:1 i 11S=k:0 k<i:b[k]. 3.Ищем предусловие для цикла: wp(“i,s:=1,b[0]”,P)=111b[0]= k:0 k<1:b[k]=Tb[0]=b[0]=T. т.е. P можно использовать в качестве постусловия. Если P истинно перед шагом цикла, то оно истинно и после него. P называется инвариантом цикла. Введем некоторую функцию t. Она показывает число шагов до конца цикла. t:11-i Такая функция называется ограничивающей функцией. В нашем случае она четко говорит сколько шагов до конца цикла. Ограничивающая функция в общем случае показывает верхнюю границу числа шагов до конца цикла. В общем случае инвариант остается неизменен. Ограничивающая функция изменяется с каждым шагом. ТЕОРЕМА О ЦИКЛЕ, ЕГО ИНВАРИАНТЕ И ОГРАНИЧИВАЮЩЕЙ ФУНКЦИИ. 1). PBiwp(Si,P) , 1 i n Т.е. если выполняется инвариант и нас пустила i-ая охрана, то выполнение i-ой команды составит истинность P. 2). PBB t >0 Т.е. если выполняется инвариант и BB, то ограничивающая функция положительна. 3). С каждым шагом ограничивающая функция уменьшается. PBiwp(“t1:=t; Si”, t<t1) ,1 i n Т.е. если нас пустила i-ая охрана, то выполнение i-ой команды уменьшит функцию. Если 1), 2), 3) – выполняется, то P можно использовать в качестве предусловия в команде повторения: Pwp(DO,PBB); АНОТИРОВАНИЕ ЦИКЛА. {Q} {inv P: инвариант} {bound t: ограничивающая функция} do B1S1 B2S2 B3S3 ………… od {R} Список условий для проверки цикла: 1). P- истинно перед началом выполнения цикла; 2). P- инвариант: {PBi}Si{P} 3). Выполнение P и невыполнение BB должно дать R: (PBB)R 4). Если цикл еще не закончен, то ограничивающая функция положительна: PBB t >0 5). С каждым шагом идет приближение к окончанию цикла: {PBi} t1:=t; Si {t<t1} Пример: {b0} x,y,z:=a,b,0; {P:y 0 z+x*y=a*b} {t:y} do y>0 четный(y)y; x:=y/2; x+x нечетный(y)y; z:=y-1; z+x od {z =a*b} ПРОЦЕДУРЫ. - используются для целей абстракции. Построение процедуры – это рассмотрение языка путем включения новой операции. Если программу рассматривать как конструктивное дказательство, то процедура будет леммой в таком доказательстве. ОПИСАНИЕ ПРОЦЕДУРЫ. {Pre: P} {Post: Q} proc <идентификатор>(<специф. параметра>;…); <тело> <специф. параметра>: value <список идент.>: тип – изменения параметров не передаются в основную программу. result <список идент.>: тип value result <список идент.>: тип - изменения параметров передаются в основную программу; значение в процедуру не передается, а только из процедуры. Есть ограничение на тело: в теле могут лишь передаваться параметры процедуры и идентификаторы, описанные в самом теле, т.е. нельзя использовать глобальные переменные. В P используются параметры с атрибутами value и value result. В Q – со словом result. ПОИСК ЭЛЕМЕНТОВ В МАССИВЕ. {Pre: n=Nx=Xb=BXB[0:N-1]} {Post:0 i NB[i]=X} proc search(value n,x:integer; value b:array of integer; result i:integer); i:=0 {Inv: 0 i NXB[0:N-1]} {Огранич. функция: N-i} do b[i]xi:=i+1 od Выносим за скоби типы и делаем обобщение: proc p(value x; value result y ; result z); - передаются формальные параметры. Где x={x1, x2,…,xn} Вызов процедуры: формальные параметры заменяются фактическими. P( a, b, c ); где а – могут быть выражениями; b,c – только переменные. АЛГОРИТМ ВЫПОЛНЕНИЯ ПРОЦЕДУРЫ. 1). Определяются значения аргументов a и b и записываются в параметры x и y: x,y:=a,b 2). Определяются переменные, описывающие b,c(под них резервируется память и т.д.). 3). Выполняется тело. 4). Значения выходных параметров y и z записываются в выходные аргументы b и c: b,c:=y,z ФОРМАЛЬНОЕ ОПРЕДЕЛЕНИЕ ПРОЦЕДУРЫ. x,y:=a,b; B; b,c:=y,z Через wp(“p(a,b,c)”,R) = wp(“x,y:=a,b; B; b,c:=y,z”,R) ТЕОРЕМА О ВЫЗОВЕ ПРОЦЕДУРЫ. {P} B {Q} {PR: P(u,v: QR)} P(a,b,c) {R} Предусловие: PRwp(p(a,b,c),R) Описание: Proc swap(value result y1,y2); {P:y1=X y2=Y} B {Q:y1=Y y2=X} Вызов: {a=X b=Y} swap(a,b) {R:a=Y b=X} Ищем предусловие того, что после выполнения swap у нас все будет нормально: PR= a=X b=Y u1, u2: u1=Y u2=X u1=Y u2=X = a=X b=Y T = (a=X b=Y) Для других аргументов: {i=I (k: b[k]=B[k])} swap(i,b[i]) {i=B[I] b[I]=I (k:Ik:b[k]=B[k])} swap(value result y1,y2) {y1=I y2=B[I]} B {y1=B[I] y2=I} По доказанной теореме, по постусловию вызова, мы получили условие и оно должно совпасть с тем, что задано: PR=i=Ib[i]=B[I]u1,u2:u1=B[I]u2=Iu1=B[I](b;i:u2)[I]k:kI:(b;i:u2)[k]= =B[k] = = i=Ib[i]=B[I]B[I]=B[I](b;i=I)[I]=Ik:kI(b;i:I)[k]=B[k] i=Ib[i]=B[I]I= Ik:kI:b[k]=B[k] i=Ik:kI:b[k]=B[k] вызов был сделан правильно, спецификация выполнилась. Задания для самостоятельной работы: 1). proc p(value x; result z1,z2); {P:x=X} z1,z2:=x,x {Q:z1=z2=X} PR: {a=X}p(a,c1,c2){c1=c2=X} 2). Proc m(value x1,x2; result z); {P:x1=X1x2=X2} if x1>x2 z:=x1 x2 x1 z:=x2 fi {Q:z=max(X1,X2)} PR={a=b=A}m (a,b,c) {R:c=A}