- •Лабораторная работа №2.
- •Часть 1. Подготовка среды
- •Лабораторная работа №2.
- •Часть 2. Массивы.
- •Лабораторная работа №2.
- •Часть 3. Описание предикатов.
- •Лабораторная работа №2.
- •Часть 4. Обращение к разным состояниям переменных.
- •Лабораторная работа №2.
- •Часть 5. Описание функций, возвращающих значение. Леммы.
- •Лабораторная работа №2.
- •Часть 5. Призрачные переменные. Поведение функции.
- •Лабораторная работа №2.
- •Часть 6. Самостоятельное задание.
Лабораторная работа №2.
Часть 4. Обращение к разным состояниям переменных.
Вставьте еще один метод в класс:
/*@ requires t != null;
@ ensures
@ \forall integer i; 0 < i < t.length ==> t[i] == \old(t[i−1]);
@*/
public static void shift(int[] t) {
/*@ loop_invariant
@ j < t.length &&
@ (\forall integer i; 0 <= i <= j ==> t[i] == \at(t[i],Pre)) &&
@ (\forall integer i;
@ j < i < t.length ==> t[i] == \at(t[i−1],Pre));
@ loop_variant j;
@*/
for (int j = t.length - 1; j > 0; j--) {
t[j] = t[j - 1];
}
}
Здесь присутствуют конструкции:
\old(…) – возвращает значение переменной до вызова метода. Может быть использована только в описании постусловия.
\at(…, Pre) – возвращает значение переменной на предыдущей итерации. Вместо Pre может быть использована метка Here, аналогично предикатам с соответствующим смыслом.
Заново откройте измененный фал для верификации и верифицируйте его.
Убедитесь, что все выражения доказаны.
Лабораторная работа №2.
Часть 5. Описание функций, возвращающих значение. Леммы.
Закройте редактор Gedit и терминал, в котором он был запущен.
Проделайте пункты 1, 2, 4 части 1. Вы должны оказаться в каталоге с вашей фамилией.
Создайте каталог для следующего примера:
mkdir Muller
Перейдите в этот катлог:
cd Muller
Создайте файл исходного кода и откройте его в редакторе Gedit:
Gedit Muller.java
Вставьте в файл следующий текст программы:
//@+ SeparationPolicy = Regions
/*@ axiomatic NumOfPos {
@ logic integer num_of_pos{L}(integer i,integer j,int t[]);
@ axiom num_of_pos_empty{L} :
@ \forall integer i j, int t[];
@ i >= j ==> num_of_pos(i,j,t) == 0;
@ axiom num_of_pos_true_case{L} :
@ \forall integer i j k, int t[];
@ i < j && t[j-1] > 0 ==>
@ num_of_pos(i,j,t) == num_of_pos(i,j-1,t) + 1;
@ axiom num_of_pos_false_case{L} :
@ \forall integer i j k, int t[];
@ i < j && ! (t[j-1] > 0) ==>
@ num_of_pos(i,j,t) == num_of_pos(i,j-1,t);
@ }
@*/
/*@ lemma num_of_pos_non_negative{L} :
@ \forall integer i j, int t[]; 0 <= num_of_pos(i,j,t);
@*/
/*@ lemma num_of_pos_additive{L} :
@ \forall integer i j k, int t[]; i <= j <= k ==>
@ num_of_pos(i,k,t) == num_of_pos(i,j,t) + num_of_pos(j,k,t);
@*/
/*@ lemma num_of_pos_increasing{L} :
@ \forall integer i j k, int t[];
@ j <= k ==> num_of_pos(i,j,t) <= num_of_pos(i,k,t);
@*/
/*@ lemma num_of_pos_strictly_increasing{L} :
@ \forall integer i n, int t[];
@ 0 <= i < n && t[i] > 0 ==>
@ num_of_pos(0,i,t) < num_of_pos(0,n,t);
@*/
public class Muller {
/*@ requires t != null;
@*/
public static int[] m(int t[]) {
int count = 0;
/*@ loop_invariant
@ 0 <= i <= t.length &&
@ 0 <= count <= i &&
@ count == num_of_pos(0,i,t) ;
@ loop_variant t.length - i;
@*/
for (int i = 0; i < t.length; i++) {
if (t[i] > 0) {
count++;
}
}
int u[] = new int[count];
count = 0;
/*@ loop_invariant
@ 0 <= i <= t.length &&
@ 0 <= count <= i &&
@ count == num_of_pos(0,i,t);
@ loop_variant t.length - i;
@*/
for (int i = 0; i < t.length; i++) {
if (t[i] > 0) {
u[count++] = t[i];
}
}
return u;
}
}
Это программа, которая из входного массива переписывает все положительные элементы в новый массив в том же порядке и возвращает этот массив.
Здесь используется директива
//@+ SeparationPolicy = Regions
Так верификатор проверяет, что память, выделенная под массивы, не пересекается.
Также демонстрируется работа с описанием в спецификации функций, возвращающих значение, за это отвечает конструкция /*@ logic t1 id{L}(t2 var, …) = e;*/, где t1 – возвращаемый тип, id – имя функции, L – метка, аналогично предикатам, t2 – тип переменной-параметра, var – имя переменной-параметра, e – выражение. В данном примере выражение e отсутствует – это не ошибка, это другой способ описания функции, т.к. необходимая нам функция нетривиальна.
Здесь описывается функция, возвращающая число положительных элементов в массиве t начина с индекса i до индекса j. Для этого описана сама функция: ее имя, список параметров – и правила, по которым происходит вычисления. Эти правила описываются после ключевого слова /*@ axiom …;*/. Все вместе это группируется в блоке /*@ axiomatic id {}*/.
Также в этом примете используются леммы. Леммы помогают верификатору работать с выражениями, доказывать их. При верификации в графической среде леммы также будут отображаться и их также можно попытаться доказать, но неизвестно корректная реакция верификатора на леммы. Они могут быть и не верифицированы, но тем не менее они помогают верификатору. Сами леммы оформляются в конструкции /*@ lemma id{L}: e;*/, где id – имя леммы, L – метка, аналогично предикатам, e – выражение.
Сохраните файл.
Откройте его для верификации.
Убедитесь, что утверждения доказаны (леммы не обязательно будут доказаны).
