Здравствуйте,при покупке печатной формы сертификата,будут ли выданы обе печатные сторны? |
Лекция 7: Формальные спецификации, доказательство и верификация программ
Разработка спецификации проводится по следующей схеме:
- Определение терминов, которыми будет специфицироваться программа.
- Описание понятий и объектов, для обозначения которых используется денотат, идентифицируемый с помощью некоторого имени (или фразы).
- Описание инвариантных свойств программы.
- Определение операций над структурами программы (например, ввести объект, удалить и др.), изменяющие ее состояние и сохранение инвариантных свойств.
При переходе от одного шага детализации к другому модель программы детализируется и постепенно становится ближе к конечному описанию. Функции - это операции, которые уточняются при детализации структуры программы на каждом шаге спецификации и описания поведения модели.
При реальном выполнении спецификация исполняется итерационно. На первом уровне проверяется только свойства модели программы при заданных ограничениях независимо от среды. Затем используется уточненная и расширенная спецификация с набором формальных утверждений. И так до тех пор, пока окончательно не будет завершен процесс пошагового доказательства спецификации.
Для демонстрации возможностей VDM языка рассмотрим задачу поиска ("Поиск") в каталоге
( ) репозитария компонентов имени компонента
и сравнения его с заданным в запросе пользователя.
В случае совпадения имен проверяются параметры, и при их совпадении из каталога извлекается код компонента и передается пользователю.
Спецификация переменных программы "Поиск"
![\begin{array}{rcrl}
repoz & :: = & developеrs :& dl \to Init - set \\
&&catalR:& cat \to Init - set \\
&&role :& inst \to facet \\
facet &:: = &autors:& Milk \\
&&title :& N \\
user &:: = &developer :& Itn \\
&&free :& Bool,
\end{array}](/sites/default/files/tex_cache/b731df9a84cbd2d7f4fcfa660320d783.png)
где - cведения о разработчике компонента
;
- переменная, в которую посылается код компонента, выбранного из каталога
репозитария
при совпадении имен в каталоге и запросе;
- переменная, в которой хранится текущий элемент из репозитария, найденный по
фасете компонента с номером
для
;
- имя разработчика компонента;
- переменная, которая используется для задания признака -
компонент не найден или к нему никто не обращался.
Описание инвариантных свойств программы
![\begin{array}{l}
type \;inv - repoz: repoz \to Bool \\
inv - repoz (dev) = \\
let\; mk \; repoz (cd, c, role) = dev\;in \\
(\forall i \in dom \; cd)\\
(\forall i \in cd (i)\\
((\exists j \in dom \;cd\; \& \;i \in c) \;\&\; \exists a \in elems\;role (i)\text{, }autors (a \in dom cd)\\
\&\; free = false \Leftrightarrow developer = catalR \;\&\; facet (N) = role \dots
\end{array}](/sites/default/files/tex_cache/e9855b61adc1634bea928ab06944305f.png)
Операторы программы проверяют список имен компонентов в каталоге, который содержит элементов типа
.
Если они совпадают с именем в запросе, результат сохраняется в
.
Доказательство инвариантных свойств программ должно проводиться автоматизированным способом с помощью специально созданных инструментальных средств поддержки VDM языка.
6.1.2. Спецификация программ средствами RAISE
RAISE-метод и RSL-спецификация (RAISE Specification Language) [6.9, 6.10] были разработаны в 80-х годах как результат предварительного исследования формальных методов и их пополнения новыми возможностями. Метод содержит нотации, техники и инструменты для конструирования программ и доказательстве их правильности. Он имеет программную поддержку в виде набора инструментов и методик, которые постоянно развиваются и используются при доказательстве правильности программ, описанных в RSL и ЯП (С++ и Паскаль). Язык RSL содержит абстрактные параметрические типы данных (алгебраические спецификации) и конкретные типы данных (модельноориентированные), подтипы, операции для задания последовательных и параллельных программ. Он предоставляет аппликативный и императивный стиль спецификации абстрактных программ, а также формальное конструирование программ в других ЯП и доказательство их правильности. Синтаксис этого языка близок к синтаксису языков С++ и Паскаль.
В RSL-языке имеются предопределенные абстрактные типы данных и конструкторы сложных типов данных, такие как произведение ( ),
множества (
), списки (
), отображения (
), записи (
) и т.п.
Далее рассмотрим некоторые конструкторы сложных типов данных.
Произведение типов - это упорядоченная конечная последовательность типов произведения (
)
.
Представитель типа имеет вид (
),
где каждое
-это значение типа
.
Компонент произведения можно получить операцией
и переслать
, т.е.
![\begin{array}{l}
get\;component(i, d) = get\;value(i, d), \\
set\;component(d, i, val) = d \Rightarrow \nabla(I \to val).
\end{array}](/sites/default/files/tex_cache/9a709c885a19ca7714f2ea249665fa10.png)
Количество компонентов произведения d находится таким образом:
![size (d) = id \nabla (null (couter inc(counter))).](/sites/default/files/tex_cache/6e6ea24cedd561bdaa31a16adc6800d1.png)
Конструктор произведения и
строит произведение
вида:
![product (d_{1}, d_{2}) = id \nabla (size(d_{1}) \Rightarrow couter\;1) \nabla (null (couter\;2) inc\; couter\; 2))).](/sites/default/files/tex_cache/3d3951c402dbd4562ac35aedf8ebfb58.png)
Для каждого конкретного типа можно построить конструктор значения этого типа из отдельных компонентов произведения таким образом:
![make\; product (value_{1}, \dots, value_{n}) =
(value_{i} \Rightarrow 1) \nabla \dots \nabla (value_{n} \Rightarrow n),](/sites/default/files/tex_cache/ebec766458fab25544df3875c19d1a31.png)
где каждое значение имеет тип
, а результирующее значение - тип произведения
Списки типов - это последовательность значений одного типа , могут быть конечным списком типов
и неконечными списком типов
.
В качестве структур данных типа списка может быть бинарное дерево,
в котором есть голова (
) и сын (
),
который следует за ним в списке, и хвост.
К операциям списка относится операция
- взятия первого элемента списка,
т.е. головы, и операция
- хвоста остальных элементов (аналогично как в VDM).
Функция выбирает из списка
-элемент.
Индекс элемента помогает выбрать нужный элемент списка:
![Index(I, idx) = L(idx) = \text{ while }(\neg \text{ is }null(idx))\text{ do }((L \Rightarrow tail \Rightarrow L) \nabla dec(idx)) L \Rightarrow Head.](/sites/default/files/tex_cache/9893205f9977032cd685d194a322ca53.png)
Для определения количества элементов в списке выполняется функция:
![\begin{array}{l}
len (L) = (ld\; \nabla\; null (result)) \\
\text{while }(L \Rightarrow)\text{ do }(( L \Rightarrow tail \Rightarrow tail \Rightarrow L)\; \nabla\; inc (result)) \\
result \Rightarrow.
\end{array}](/sites/default/files/tex_cache/fc638930021e3f49526c692518e6d3b2.png)
Элемент списка находится так:
![\begin{array}{l}
elem (L) = (ld\; \nabla \;empty (result)) \\
\text{while }(L \Rightarrow)\text{ do }(( L \Rightarrow tail \Rightarrow L)\; \nabla \\
(result \uparrow ( L \Rightarrow head \Rightarrow) \Rightarrow elem ) \Rightarrow result) \\
result \Rightarrow.
\end{array}](/sites/default/files/tex_cache/970d2d26edf50b8b6cf72039e3a71c42.png)
Аналогично можно представить функции конкатенации, преобразование типов данных, добавления элемента в голову и хвост списка и др.
Отображение - это структура ( ),
которая ставит в соответствие значениям одного типа значение другого типа.
Вместе с тем отображение - это бинарное отношение декартова произведения двух множеств как совокупности двухкомпонентных пар,
в которых первый компонент -
содержит элементы аргументов отображения,
а второй компонент
- соответствующие элементы значений этого отображения.
В языке имеются разные допустимые операции над отображениями: наложение, объединение, композиция, срез и др. Среди этих видов отношений рассмотрим,
например, композицию отображений ( ,
):
![\begin{array}{l}
(ld\;\nabla\; (compose (m_{1}, m_{2}) \Rightarrow m))\;apply\;(m, elem) \\
apply\; to\; composition\;(m_{1}, m_{2}, elem) =\\
=(ld\; \nabla\; (image (elem, m1)\Rightarrow s)\;restrict\;(m_{2}, s) \Rightarrow map \\
(ld\; \nabla\; (map\text{ getname }elem \Rightarrow name))\;getvalue\;(name, map).
\end{array}](/sites/default/files/tex_cache/a5507d7785445db277bc5a57a54adc8e.png)
При этом используются функции:
![\begin{array}{l}
\text{Apply (}m\text{, elem) = image (elem,}m)\text{ elem }\Rightarrow, \\
\text{Apply (}m\text{, elem) = getvalue (elem,}m)\text{ elem }\Rightarrow.
\end{array}](/sites/default/files/tex_cache/8653b27516a72329f43a45b946544a4b.png)
Запись - это совокупность именованных полей.
Этот тип соответствует типу в языке Паскаль и
в языке С++.
В языке RAISE для записи определено два конструктора -
,
,
описание которых имеет вид
![\begin{array}{l}
type\; record\; id =\\
type\; mk\_id\; (short _record\; id ) ::=\\
destr\_id_{1} : type\_expr_{1} \leftrightarrow recon\_id\\
\;\;\;\;\;\;\dots\\
destr\_id_{n} : type\_expr_{n} \leftrightarrow recon\_id.
\end{array}](/sites/default/files/tex_cache/1e3c40dddf563305f8b019596222e4c6.png)
Идентификатор - это конструктор типа
, для которого задается деструктор
как функции получения значения компонентов записи.
Объединение - это конструктор для объединения типов
,
при котором тип
получает одно из значений
в списке элементов.
Конструктор типа имеет вид
![\text{type }id = id \text{\_from\_} id_{1}(id \text{\_to\_} id_{1:} id_{1})\;|\;\dots\;| \;
id \text{\_from\_} id_{n}(id\text{\_to\_}idn_{:} id_{n}).](/sites/default/files/tex_cache/7dfa4aad9739b6c7c099bbb6fb0d9d30.png)
Операции над самим типом не определены в языке RAISE.
Рассмотренные формальные структуры данных языков VDM и RAISE предназначены для математического описания программ с помощью утверждений и конструирования новых структур данных, необходимых для проектируемых программ. Средства этих языков фактически - элементы спецификации программ, по которым проще проверять правильность программ методами верификации или доказательства, составляя при этом, как и в случае VDM, пред, постусловия и утверждения для проведения доказательства программы по ее спецификациям.