Семантическая теория программ
Денотационная семантика как наиболее строгий и широко известный метод описания значения программ. Знакомство с основными особенностями определения семантики языка программирования. Общая характеристика методов доказательства правильности программ.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | курсовая работа |
Язык | русский |
Дата добавления | 18.01.2015 |
Размер файла | 142,7 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Семантическая теория программ
Введение
Существует несколько причин, по которым следует заниматься описанием семантики программ, или смысла выражений, операторов и программных единиц.
Руководство по использованию языка программирования должно включать описание каждой конструкции языка, как по отдельности, так и в совокупности с другими конструкциями. В языке имеется множество различных конструкций, точное определение которых необходимо как программисту, использующему язык, так и разработчику реализации этого языка. Программисту эти сведения нужны для того, чтобы писать правильные программы и заранее знать результат выполнения любых операторов программы. Разработчику компилятора корректные определения конструкций необходимы для создания правильной реализации языка.
В большинстве руководств определение семантики дается в виде обычного текста. Как правило, сначала при помощи какой-либо формальной грамматики дается определение синтаксиса конструкции, а затем для пояснения семантики приводятся несколько примеров и небольшой пояснительный текст. К сожалению, смысл этого текста часто неоднозначен, так что разные читатели могут понимать его по-разному. Программист может получить ошибочное представление о том, что именно будет делать написанная им программа при выполнении, а разработчик может реализовать какую-либо языковую конструкцию иначе, чем разработчики других реализаций того же языка. Как и для синтаксиса, нужен какой-то метод, позволяющий дать удобочитаемое, точное и лаконичное определение семантики языка.
Задача определения семантики языка программирования рассматривается теоретиками давно, но до сих пор не найдено удовлетворительного универсального решения. Было разработано множество различных методов формального определения семантики. Ниже приводится описания некоторых из них.
1.Операционная семантика
Операционная семантика, сводится к описанию смысла программы посредством выполнения ее операторов на реальной или виртуальной машине. Смысл оператора определяется изменениями, произошедшими в состоянии машины после выполнения данного оператора. Для того чтобы разобраться в этой концепции, рассмотрим команду на машинном языке. Пусть состояние компьютера - это значения всех его регистров и ячеек памяти, в том числе коды условий и регистры состояний. Если просто записать состояние компьютера, выполнить команду, смысл которой нужно определить, а затем изучить новое состояние машины, то семантика этой команды станет понятной: она представляется изменением в состоянии компьютера, вызванным выполнением команды.
Описание операционной семантики операторов языков программирования высокого уровня требует создания реального или виртуального компьютера. Аппаратное обеспечение компьютера является чистым интерпретатором его машинного языка. Чистый интерпретатор любого языка программирования может быть создан с помощью программных средств, которые становятся виртуальным компьютером для данного языка. Семантику языка высокого уровня можно описать, используя чистый интерпретатор данного языка. При таком подходе, правда, существуют две проблемы. Во-первых, сложность и индивидуальные особенности аппаратного обеспечения компьютера и операционной системы, используемых для запуска чистого интерпретатора, затрудняют понимание происходящих действий. Во-вторых, выполненное таким образом семантическое определение будет доступно только для людей с абсолютно идентичной конфигурацией компьютера. Этой проблемы можно избежать, заменив реальный компьютер виртуальным компьютером низкого уровня. Регистры, память, информация о состоянии и процесс выполнения операторов - все это можно смоделировать, соответствующими программами. Набор команд можно создать так, чтобы семантику каждой отдельной команды было легко понять и описать. Таким образом, машина была бы идеализирована и значительно упрощена, что облегчило бы понимание изменений ее состояния. Использование операционного метода для полного описания семантики языка программирования L требует создания двух компонентов. Во-первых, для преобразования языка L в операторы выбранного языка низкого уровня нужен транслятор. Во-вторых, для этого языка низкого уровня необходима виртуальная машина, состояние которой изменяется с помощью команд, полученных при трансляции операторов высокого уровня. Именно изменения состояния этой виртуальной машины определяет смысл данного оператора. Семантику конструкции for языка С можно описать в терминах следующих простых команд.
Пример 2.1. Оператор языка С Операционная семантика
Человек, читающий подобное описание, является «виртуальным компьютером» и предполагается способным правильно «выполнить» команды описания и распознать эффект такого «выполнения».
В качестве примера низкоуровневого языка, который можно применять для операционной семантики, рассмотрим следующий список операторов, соответствующих простым управляющим операторам типичного языка программирования:
Здесь relop - одни из операторов отношений из набора {= , <>, >, <, >=, <=}, ident - идентификатор, a var - идентификатор или константа. Все эти операторы просты и легки для понимания и реализации. Незначительное обобщение приведенных выше трех операторов присваивания позволяет описывать более общие арифметические выражения и операторы присваивания. Например:
ident := var bin_op var
ident := un_op var
Здесь bin_op - бинарный арифметический оператор, a un_op - унарный оператор. Многочисленные арифметические типы данных и автоматическое преобразование типов, конечно, несколько усложняют это обобщение. Введение небольшого количества новых относительно простых команд позволит описывать семантику массивов, записей, указателей и подпрограмм. Описание операционной семантики функций рассмотрим на примере системы равенств:
f1(x1, x2, ... , xk) = E1,
f2(x1, x2, ... , xk) = E2,
. . . . . . . . . . . . . (2.1)
fm(x1, x2, ... , xk) = Em,
где в левых частях равенств явно указаны определяемые функции с формальными параметрами, включающими обозначения всех входных данных x1, x2, ... , xk, а правые части представляют собой выражения, содержащие, вообще говоря, вхождения этих функций с аргументами, задаваемыми некоторыми выражениями, зависящими от входных данных x1, x2, ... , xk. Операционная семантика интерпретирует эти равенства как систему подстановок. Под подстановкой <s, E, t> терма t в выражение E вместо символа s (в частности, переменной) будем понимать переписывание выражения E с заменой каждого вхождения в него символа s на выражение t. Каждое равенство fi(x1, x2, ... , xk) = Ei задает в параметрической форме множество правил подстановок:
где t1,--t2, ... , tk - конкретные аргументы (значения или определяющие их выражения) данной функции. Это правило допускает замену вхождения левой его части в какое-либо выражение на его правую часть.
Интерпретация системы равенств (2.1) для получения значений определяемых функций в рамках операционной семантики производится следующим образом. Пусть задан набор входных данных (аргументов) d1, d2,... ,dk. На первом шаге осуществляется подстановка этих данных в левые и правые части равенств с выполнением там, где это возможно, предопределенных операций и с выписыванием получаемых в результате этого равенств. На каждом следующем шаге просматриваются правые части полученных равенств.
Если правая часть является каким-либо значением, то оно и является значением функции, указанной в левой части этого равенства. В противном случае правая часть является выражением, содержащим вхождения каких-либо определяемых функций с теми или иными наборами аргументов. Если для такого вхождения соответствующая функция с данным набором аргументов имеется в левой части какого-либо из полученных равенств, то либо вместо этого вхождения подставляется вычисленное значение правой части этого равенства, либо не производится никаких изменений.
В том же случае, если эта функция с данным набором аргументов не является левой частью никакого из полученных равенств, то формируется (и дописывается к имеющимся) новое равенство. Оно получается из исходного равенства для данной функций подстановкой в него вместо параметров указанных аргументов этой функции. Такие шаги осуществляются до тех пор, пока все определяемые функции не будут иметь вычисленные значения.
Пример 2.2. Рассмотрим систему равенств, определяющую функцию FACT(n) = n!
FACT(0) = 1,
FACT(n) = nFACT(n-1).
Для вычисления значения FACT(3) осуществляются следующие 6 шагов.
1-й шаг: 2-й шаг: 3-й шаг:
FACT(0) = 1 FACT(0) = 1 FACT(0) = 1
FACT(3) = 3FACT(2). FACT(3) = 3FACT(2) FACT(3) = 3FACT(2)
FACT(2) = 2FACT(1). FACT(2)= 2FACT(1)
FACT(1)= 1FACT(0).
4-й шаг: 5-й шаг: 6-й шаг:
FACT(0) = 1 FACT(0) = 1 FACT(0) = 1
FACT(3) = 3FACT(2) FACT(3) = 3FACT(2) FACT(3) = 6
FACT(2) = 2FACT(1) FACT(2) = 2 FACT(2) = 2
FACT(1) = 1. FACT(1) = 1. FACT(1) = 1.
Первым и самым значительным использованием формальной операционной семантики было описание семантики языка PL/I. Эта абстрактная машина и правила трансляции языка PL/I были названы общим именем Vienna Definition Language (VDL) в честь города, в котором они были созданы корпорацией IBM.
Операционная семантика является эффективной до тех пор, пока описание языка остается простым и неформальным. К сожалению, описание VDL языка PL/I настолько сложно, что практическим целям оно фактически не служит.
Операционная семантика зависит от алгоритмов, а не от математики. Операторы одного языка программирования описываются в терминах операторов другого языка программирования, имеющего более низкий уровень. Этот подход может привести к порочному кругу, когда концепции неявно выражаются через самих себя. Методы, описываемые в следующих двух разделах, значительно более формальны в том смысле, что они опираются на логику и математику, а не на машины.
2.Аксиоматическая семантика
Аксиоматическая семантика была создана в процессе разработки метода доказательства правильности программ. Данный метод распространяет на программы область применения исчисления предикатов. Семантику каждой синтаксической конструкции языка можно определить как некий набор аксиом или правил вывода, который можно использовать для вывода результатов выполнения этой конструкции. Чтобы понять смысл всей программы (то есть разобраться, что и как она делает), эти аксиомы и правила вывода следует использовать так же, как при доказательстве обычных математических теорем. В предположении, что значения входных переменных удовлетворяют некоторым ограничениям, аксиомы и правила вывода могут быть использованы для получения (вывода) ограничений на значения других переменных после выполнения каждого оператора программы. В конце концов, когда программа выполнена, мы получаем доказательство того, что вычисленные результаты удовлетворяют необходимым ограничениям на их значения относительно входных значений. То есть, доказано, что выходные данные представляют значения соответствующей функции, вычисленной по значениям входных данных.
Такие доказательства показывают, что программа выполняет вычисления, описанные ее спецификацией. В доказательстве каждый оператор программы сопровождается предшествующим и последующим логическими выражениями, устанавливающими ограничения на переменные в программе. Эти выражения используются для определения смысла оператора вместо полного описания состояния абстрактной машины (как в операционной семантике).
3.Краткое введение в исчисление высказываний
Аксиоматическая семантика основана на математической логике. Будем называть предикат, помещенный в программу высказыванием (иногда употребляют термин утверждение). Высказывание, непосредственно предшествующее оператору программы, описывает ограничения, наложенные на переменные в данном месте программы. Высказывание, следующее непосредственно за оператором программы, описывает новые ограничения на те же (а возможно, и другие) переменные после выполнения оператора.
Обычно термин высказывание используется для логических переменных, принимающих одно из двух значений F и Т, которые представляют понятия «ложь» и «истина». На значениях типа «логический» определены пять операций: отрицание: (NOT b), конъюнкция: (b AND с), дизъюнкция: (b OR с), импликация: (b с), равенство: (b = с). Высказывания строятся по следующим правилам.
1. F и Т - высказывания.
2. Идентификаторы (непустая последовательность букв и цифр, начинающаяся с буквы) являются высказываниями.
3. Если b - высказывание, то (NOT b) высказывание.
4. Если b и с - высказывания, то (b OR с), (b AND с), (b = с), (b с) - также высказывания.
Вычисление постоянных высказываний, содержащих в качестве операндов только константы, имеет три определяемых структурой высказывания E случая: E без операций, E с одной операцией, E с более чем одной операцией.
1. Значение Т есть Т, значение F есть F.
2. Значения (NOT b), (b AND с), (b OR с), (b с), (b = с), где b и с - константы F и Т в любой комбинации, определяются по таблице, называемой таблицей истинности.
Таблица 1
3. Значение постоянного высказывания, содержащего более чем одну операцию, получается последовательным применением таблицы истинности к подвысказываниям этого высказывания и заменой подвысказываний на их значения до тех пор, пока высказывание не сведется к F или Т.
Состояние s - это функция из множества идентификаторов в множество значений F и Т. Высказывание E определено в состоянии s, если каждый идентификатор из E встречается в s. s(E) - значение E в состоянии s - получаемое заменой всех вхождений идентификаторов b в высказывание E на их значения s(b) и вычислением получившегося постоянного высказывания.
Тавтология - высказывание, истинное в любом состоянии, в котором оно определено.
Высказывание выражает, или описывает, множество состояний, в котором оно истинно.
Правила старшинства операций:
1. Последовательность одноименных операций вычисляется слева направо.
2. Порядок вычислений различных между собой и смежных в записи высказывания операций определяется списком: NOT, AND, OR, , =.
Вычисление высказываний редко бывает самоцелью. Чаще необходимо манипулировать ими, чтобы вывести «эквивалентные», но более простые высказывания.
Высказывания Е1 и Е2 эквивалентны, если и только если Е1 = Е2 есть тавтология. Здесь Е1 ~ Е2 эквивалентность.
1. Законы коммутативности: (Е1 Е2) = (Е2 Е1), - обозначает операции = , AND, OR.
2. Законы ассоциативности: (Е1 Е2) Е3 = Е1 (Е2 Е3), - обозначает операции AND, OR.
3. Законы дистрибутивности: (Е1--Е2)--Е--Е3--=--(Е1--Е--Е3)--(Е2--Е--Е3), и Е - обозначают операции AND, OR.
4. Законы де Моргана: NOT (Е1 Е2) = NOT Е1--Е--NOT--Е1),--и--Е---обозначают операции AND, OR.
5. Закон отрицания: NOT (NOT Е1) = Е1.
6. Закон исключенного третьего: Е1 OR NOT Е1 = Т.
7. Закон противоречия: Е1 AND NOT Е1 = F.
8. Закон импликации: : (Е1 Е2) = NOT Е1 OR Е2.
9. Закон равенства: (Е1 = Е2) = (Е1 Е2) AND (Е2 Е1).
10. Законы упрощения OR: Е1 OR Е1 = Е1, Е1 OR (Е1 AND Е2) = Е1, Е1 OR Т = Т, Е1 OR F = Е1.
11. Законы упрощения AND: Е1 AND Е1 = Е1, Е1 AND (Е1 OR Е2) = Е1, Е1 AND Т = Е1, Е1 AND F = F.
12. Закон тождества Е1 = Е1.
Для вычисления высказываний используют правила подстановки и транзитивности.
Правило постановки: Если е1 = е2 - эквивалентность, а Е(х) - высказывание, записанное как функция от одного из своих идентификаторов х. Тогда Е(е1) = Е(е2) и Е(е2) = Е(е1) эквивалентности.
Правило транзитивности: Если е1 = е2 и е2 = е3 - эквивалентности, то е1 = е3 - эквивалентности.
Для формулирования развернутых высказываний используются кванторы.
Квантор существования (?i: m i < n: Ei).
Квантор всеобщности (?i: m i < n: Ei) = NOT (?i: m i < n: NOT Ei)
Множество значений, удовлетворяющих m i < n, называется областью значений квантифицированной переменной i.
Вхождение идентификатора i в предикат называется связанным, если оно находится в области действия квантора ?i или ?i, и свободным в противном случае. В одном и том же выражении один и тот же идентификатор не может быть как связанным, так и свободным и идентификатор не может быть связан двумя различными кванторами.
4.Преобразователь предикатов
Введем обозначение (триада Хоара)
{Q} S {R}, (2.2)
где Q, R - предикаты, S - программа (оператор или последовательность операторов). Обозначение определяет следующий смысл: «Если выполнение S началось в состоянии, удовлетворяющем Q, то имеется гарантия, что оно завершится через конечное время в состоянии, удовлетворяющем R».
Предикат Q называется предусловием или входным высказыванием S, предикат R - постусловием или выходным высказыванием. Следовательно, R определяет то, что нужно установить. Можно сказать, что R определяет спецификацию задачи. В предусловии Q нужно отражать тот факт, что входные переменные получили начальные значения.
В дальнейшем при изучении высказываний мы будем предполагать, что предусловия операторов вычисляются на основе постусловий, хотя этот процесс можно рассматривать и с противоположной точки зрения.
Пример 2.3. Рассмотрим оператор присваивания для целочисленных переменных и постусловие:
sum := 2 * х + 1 {sum > 1}
Одним из возможных предусловий данного оператора может быть {х > 10}.
Слабейшими предусловиями называются наименьшие предусловия, обеспечивающие выполнение соответствующего постусловия. Например, для приведенного выше оператора и его постусловия предусловия {х > 10}, {х > 50} и {х > 1000} являются правильными. Слабейшим из всех предусловий в данном случае будет {х > 0}.
Э. Дейкстра рассматривает слабейшие предусловия, т.е. предусловия, необходимые и достаточные для гарантии желаемого результата.
«Условие, характеризующее множество всех начальных состояний, при которых запуск обязательно приведет к событию правильного завершения, причем система (машина, конструкция) останется в конечном состоянии, удовлетворяющем заданному постусловию, называется слабейшим предусловием, соответствующим этому постусловию».
Условие называют слабейшим, так как чем слабее условие, тем больше состояний удовлетворяют ему. Наша цель - охарактеризовать все возможные начальные состояния, которые приведут к желаемому конечному состоянию.
Если S - некоторый оператор (последовательность операторов), R - желаемое постусловие, то соответствующее слабейшее предусловие будем обозначать wp(S, R).
Аббревиатура wp определяется начальными буквами английских слов weakest (слабейший) и precondition (предусловие). Предполагается, что известно, как работает оператор S (известна семантика S), если можно вывести для любого постусловия R соответствующее слабейшее предусловие wp(S, R).
Определение семантики оператора дается в виде правила, описывающего, как для любого заданного постусловия R можно вывести соответствующее слабейшее предусловие wp(S, R).
Для фиксированного оператора S такое правило, которое по заданному предикату R вырабатывает предикат wp(S, R), называется «преобразователем предикатов»: {wp(S, R)} S {R}.
Это значит, что описание семантики оператора S представимо с помощью преобразователя предикатов. Применительно к конкретным S и R часто бывает неважным точный вид wp(S, R), бывает достаточно более сильного условия Q, т.е. условия, для которого можно доказать, что высказывание Q => wp(S, R) справедливо для всех состояний. Значит, множество состояний, для которых Q - истина, является подмножеством того множества состояний, для которых wp(S, R) - истина. Возможность работать с предусловиями Q, не являющимися слабейшими, полезна, поскольку выводить wp(S, R) явно не всегда практично.
Сформулируем свойства (по Э. Дейкстра) wp(S, R).
Свойство 1. wp(S, F) = F для любого S. (Закон исключенного чуда).
Свойство 2. Закон монотонности. Для любого S, предикатов P и R таких, что P => R для всех состояний, справедливо для всех состояний wp(S, P) => wp(S, R).
Свойство 3. Дистрибутивность конъюнкции. Для любых S, P, R справедливо
wp(S, P) AND wp(S, R) = wp(S, P AND R).
Свойство 4. Дистрибутивность дизъюнкции. Для любых S, P, R справедливо
wp(S, P) OR wp(S, R) = wp(S, P OR R).
Если для каждого оператора языка по заданным постусловиям можно вычислить слабейшее предусловие, то для программ на данном языке может быть построено корректное доказательство. Доказательство начинается с использования результатов, которые надо получить при выполнении программы, в качестве постусловия последнего оператора программы, и выполняется с помощью отслеживания программы от конца к началу с последовательным вычислением слабейших предусловий для каждого оператора. При достижении начала программы первое ее предусловие отражает условия, при которых программа вычислит требуемые результаты.
Для некоторых операторов программы вычисление слабейшего предусловия на основе оператора и его постусловия является достаточно простым и может быть задано с помощью аксиомы. Однако, как правило, слабейшее предусловие вычисляется только с помощью правила логического вывода, т.е. метода выведения истинности одного высказывания на основе значений остальных высказываний.
5.Аксиоматическое определение операторов языка программирования в терминах wp
Определим слабейшее предусловие для основных операторов: оператора присваивания, составного оператора, оператора выбора и оператора цикла.
Оператор присваивания имеет вид: x := E, где x - простая переменная, E - выражение (типы x и E совпадают).
Определим слабейшее предусловие оператора присваивания как Q = wp(x := E, R), где Q получается из R заменой каждого вхождения x на E, что обозначим Q = RxЕ.
Предполагается, что значение Е определено и вычисление выражения Е не может изменить значения ни одной переменной. Последнее ограничение запрещает функции с побочным эффектом. Следовательно, можно использовать обычные свойства выражений такие, как ассоциативность, коммутативность и логические законы.
Составной оператор имеет вид: begin S1; S2; ... ; Sn end
Определим слабейшее предусловие для последовательности двух операторов:
wp(S1;S2, R) = wp(S1, wp(S2, R)).
Аналогично слабейшее предусловие определяется для последовательности из n операторов.
Оператор выбора определим так: if B1 > S1 П B2 > S2 ... П Bn > Sn fi
Здесь n -- 0, B1, B2, ..., Bn - логические выражения, называемые охранами, S1, S2, ..., Sn - операторы, пара Bi > Si называется охраняемой командой, П - разделитель, if и fi играют роль операторных скобок.
Выполняется оператор следующим образом.
Проверяются все Bi. Если одна из охран не определена, то происходит аварийное завершение. Далее, по крайней мере, одна из охран должна иметь значение истина, иначе выполнение завершается аварийно.
Выбирается одна из охраняемых команд Bi > Si, у которой значение Bi истина, и выполняется Si.
Определим слабейшее предусловие:
wp(if, R) = BB AND (B1 => wp(S1, R)) AND ... AND (Bn => wp(Sn, R)),
где BB = B1 OR B2 OR ... OR Bn.
Предполагается, что охраны являются всюду определенными функциями (значение их определено во всех состояниях).
Естественно определить wp(if, R) с помощью кванторов:
wp(if, R) = (? i: 1 i n : Bi) AND (?i: 1 i n : Bi => wp(Si, R)).
Пример 2.4. Определить z = |x|.
С использованием оператора выбора :if x -- 0 > z := x П x 0 > z := -x fi.
К особенностям оператора выбора следует отнести, во-первых, тот факт, что он включает условный оператор (if... then..), альтернативный оператор (if… then... else...) и оператор выбора (case).
Во-вторых, оператор выбора не допускает умолчаний, что помогает при разработке сложных программ, так как каждая альтернатива представлена подробно, и возможность что-либо упустить уменьшается.
В-третьих, благодаря отсутствию умолчаний, запись оператора выбора представлена в симметричном виде.
Оператор цикла. В обозначениях Э. Дейкстры цикл имеет вид: do B > S od.
Обозначим это соотношение через DO и представим его в следующем виде:
DO: do B1 > S1 П B2 > S2 ... П Bn > Sn od,
где n ----0, Bi > Si - охраняемые команды.
Выполняется оператор следующим образом. Пока возможно выбирается охрана Bi со значением истина, и выполняется соответствующий оператор Si. Как только все охраны будут иметь значение ложь, выполнение DO завершится.
Выбор охраны со значением истина и выполнение соответствующего оператора называется выполнением шага цикла. Если истинными являются несколько охран, то выбирается любая из них.
Следовательно, оператор DO эквивалентен оператору
do BB > if B2 > S1 П B2 > S2 ... П Bn > Sn fi od или do BB > IF od,
где BB - дизъюнкция охран, IF - оператор выбора.
Пример 2.5. Алгоритм Евклида.
Вариант 1.
задать (N, M);
if N > 0 AND M > 0 > n, m := N, M;
do n ? m > if n > m > n := n - m П m > n > m := m - n fi od;
выдать (n)
fi.
Вариант 2.
задать (N, M);
if N > 0 AND M > 0 > n, m := N, M;
do n > m > n := n - m П m > n > m := m - n od;
выдать (n)
fi.
Дадим формальное определение слабейшего предусловия для оператора цикла DO.
Пусть предикат H0(R) определяет множество состояний, в которых выполнение DO завершается за 0 шагов (в этом случае все охраны с самого начала ложны, после завершения R имеет значение истина):
H0(R) = NOT BB AND R.
Другими словами, требуется, чтобы оператор цикла DO завершил работу, не производя выборки охраняемой команды, что гарантируется первым конъюнктивным членом предиката H0(R): NOT BB = T. При этом истинность R до выполнения DO является необходимым условием для истинности R после выполнения DO.
Определим предикат Hk(R) как множество состояний, в которых выполнение DO заканчивается за k шагов при значении R истина (Hk(R) будет определяться через Hk-1(R)):
Hk(R) = H0(R) OR wp(IF, Hk-1(R)), k > 0 > wp(DO, R) = (? k: k 0: Hk(R)).
Это значит, что должно существовать такое значение k, что потребуется не более чем k шагов, для обеспечения завершения работы в конечном состоянии, удовлетворяющем постусловию R.
Определение DO. Если предикаты Hk(R) задаются в виде
Hk(R) = NOT B AND R, k = 0,
Hk(R) = wp(IF, Hk-1(R)), k > 0, > wp(DO, R)=(? k: k 0: Hk(R)).
Основная теорема для оператора цикла. Пусть оператор выбора IF и предикат P таковы, что для всех состояний справедливо
(P AND BB) => wp(IF, R). (2.3)
Тогда для оператора цикла справедливо:
(P AND wp(DO, T)) => wp(DO, P AND NOT BB).
Эта теорема известна как основная теорема инвариантности для цикла. Предикат Р, истинный перед выполнением и после выполнения каждого шага цикла, называется инвариантным отношением или просто инвариантом цикла. В математике термин «инвариантный» означает не изменяющийся под воздействием совокупности рассматриваемых математических операций.
Поясним смысл теоремы. Условие (2.3) означает, что если предикат P первоначально истинен и одна из охраняемых команд выбирается для выполнения, то после ее выполнения P сохранит значение истинности. После завершения оператора, когда ни одна из охран не является истиной, будем иметь:
P AND NOT BB
Работа завершится правильно, если условие wp(DO, T) справедливо и до выполнения DO. Так как любое состояние удовлетворяет T, то wp(DO, T) является слабейшим предусловием для начального состояния такого, что запуск оператора цикла DO приведет к правильно завершаемой работе.
Доказательство.
Из определения DO следует:
H0(T) = NOT BB AND T, k = 0;
Hk(T) = wp(IF, Hk-1(T)), k > 0, > wp(DO, T) = (? k: k -- 0: Hk(T));
H0(P AND NOT BB) = P AND NOT BB;
Hk(P AND NOT BB) = wp(IF, Hk-1(P AND NOT BB), > wp(DO, P AND NOT BB) = = (? k -- 0: Hk(P AND NOT BB)).
С помощью метода математической индукции можно доказать, что из условия (2.3) следует
(P AND Hk(T)) => Hk(P AND NOT BB), k -- 0.
Тогда имеем
P AND wp(DO, T) = (? k: k -- 0: P AND Hk(T)) => (? k: k -- 0: Hk(P AND NOT BB)) = = wp(DO, P AND NOT BB).
Следовательно, (P AND wp(DO, T)) => wp(DO, P AND NOT BB).
Чтобы использовать аксиоматическую семантику с данным языком программирования для доказательства правильности программ или для описания формальной семантики, для каждого вида операторов языка должны быть определены аксиомы или правила логического вывода. С ними мы познакомимся в п. 2.3 «Верификация программ».
В аксиоматической семантике система (2.1) интерпретируется как набор аксиом в рамках некоторой формальной логической системы, в которой есть правила вывода и/или интерпретации определяемых объектов.
Для интерпретации системы функций (2.1) вводится понятие аксиоматического описания <S, A> - логически связанной пары понятий: S - сигнатура используемых в системе символов функций f1, f2, ..., fm и символов констант (нульместных функциональных символов) c1, c2, ..., cl, а A - набор аксиом, представленный системой. Предполагается, что каждая переменная xi, i = 1, ..., k, и каждая константа ci, i = 1, ..., l, используемая в A, принадлежит к какому-либо из типов данных t1, t2, ..., tr, а каждый символ fj, j = 1, ..., m, представляет функцию, типа: ti1 ti2 t... ik ti0. Такое аксиоматическое описание получит конкретную интерпретацию, если будут заданы конкретные типы данных ti = ti', i = 1, ..., r, и конкретные значения констант ci = ci', i = 1, ..., l.
К. Хоaр построил аксиоматическое определение набора типов данных, которые потом Н. Вирт использовал при создании языка Паскаль.
Пример 2.6. Рассмотрим систему равенств:
УДАЛИТЬ(ДОБАВИТЬ(m,d)) = m,
ВЕРХ(ДОБАВИТЬ(m,d)) = d,
УДАЛИТЬ(ПУСТ) = ПУСТ,
ВЕРХ(ПУСТ) = ДНО,
где УДАЛИТЬ, ДОБАВИТЬ, ВЕРХ - символы функций, а ПУСТ и ДНО - символы констант, образующие сигнатуру этой системы. Пусть D, D1 и М - некоторые типы данных, такие, что m M, d D, ПУСТ M, ДНО D1, а функциональные символы представляют функции следующих типов:
УДАЛИТЬ: M M,
ДОБАВИТЬ: M, D M,
ВЕРХ: M D1.
Данная сигнатура вместе с указанной системой равенств, рассматриваемой как набор аксиом, образует аксиоматическое описание абстрактного типа данных, называемого магазином.
Зададим интерпретацию символов ее сигнатуры: D - множество значений, которые являются элементами магазина, M - множество состояний магазина, M = {d1,d2, ... ,dn: di D, i = 1, ..., n, n 0}, D1=D {ДНО}, ПУСТ={}, а ДНО - особое значение (зависящее от реализации магазина), не принадлежащее D. Тогда указанный набор аксиом определяют свойства магазина.
При определении семантики полного языка программирования с использованием аксиоматического метода для каждого вида операторов языка должны быть сформулированы аксиома или правило логического вывода. Но определение аксиом и правил логического вывода для некоторых операторов языков программирования - очень сложная задача. Трудно построить «множество основных аксиом, достаточно ограниченное для того, чтобы избежать противоречий, но достаточно богатое для того, чтобы служить отправной точкой для доказательства высказываний о программах» (Э. Дейкстра).
Напрашивающимся решением такой проблемы является разработка языка, подразумевающего использование аксиоматического метода, т.е. содержащей только те операторы, для которых могут быть написаны аксиомы или правила логического вывода. К сожалению, подобный язык оказался бы слишком маленьким и простым что отражает нынешнее состояние аксиоматической семантики как науки.
Аксиоматическая семантика является мощным инструментом для исследований в области доказательств правильности программ, она также создает великолепную основу для анализа программ, как во время их создания, так и позднее. Однако ее полезность при описании содержания языков программирования весьма ограничена как для пользователей языка, так и для разработчиков компиляторов.
6.Денотационная семантика
Денотационная семантика - самый строгий широко известный метод описания значения программ. Она прочно опирается на теорию рекурсивных функций. Всестороннее рассмотрение денотационной семантики - длительное и сложное дело. Здесь мы познакомимся лишь с ее основными принципами.
Основной концепцией денотационной семантики является определение для каждой сущности языка некоего математического объекта и некоей функции, отображающей экземпляры этой сущности в экземпляры этого математического объекта. Поскольку объекты определены строго, то они представляют собой точный смысл соответствующих сущностей. Сама идея основана на факте существования строгих методов оперирования математическими объектами, а не конструкциями языков программирования. Сложность использования этого метода заключается в создании объектов и функций отображения. Название метода «денотационная семантика» происходит от английского слова denote (обозначать), поскольку математический объект обозначает смысл соответствующей синтаксической сущности.
Для введения в денотационный метод мы используем очень простую языковую конструкцию - двоичные числа. Синтаксис этих чисел можно описать следующими грамматическими правилами:
<двоичное_число> > 0;
| 1;
| <двоичное_число> 0;
| <двоичное_число> 1.
Для описания двоичных чисел с использованием денотационной семантики и грамматических правил, указанных выше, их фактическое значение связывается с каждым правилом, имеющим в своей правой части один терминальный (основной) символ. Объектами в данном случае являются десятичные числа. В нашем примере значащие объекты должны связываться с первыми двумя правилами. Остальные два правила являются, в известном смысле, правилами вычислений, поскольку они объединяют терминальный символ, с которым может ассоциироваться объект, с нетерминальным, который может представлять собой некоторую конструкцию. Пусть область определения семантических значений объектов представляет собой множество неотрицательных десятичных целых чисел Nat. Это именно те объекты, которые мы хотим связать с двоичными числами. Семантическая функция Мb отображает синтаксические объекты в объекты множества N согласно указанным выше правилам. Сама функция Мb определяется следующим образом:
Мb('0') = 0, Мb('1')=1;
Мb(<двоичное_число> '0') = 2 Ч Мb(<двоичное_число>);
Мb(<двоичное_число> `1') = + 2 Ч Мb(<двоичное_число>) + 1.
Мы заключили синтаксические цифры в апострофы, чтобы отличать их от математических цифр. Отношение между этими категориями подобно отношениям между цифрами в кодировке ASCII и математическими цифрами. Когда программа считывает число как строку, то прежде, чем это число сможет использоваться в программе, оно должно быть преобразовано в математическое число.
Пример 2.7. Описание значения десятичных синтаксических литеральных констант.
<десятичное_число> > 0|1|2|3|4|5|6|7|8|9;
| <десятичное_число> (0|1|2|3|4|5|6|7|8|9).
Денотационные отображения для этих синтаксических правил имеют следующий вид:
Md(`0') = 0, Md('1') = 1, ,..., Md('9') = 9;
Мd(<десятичное_число> '0') = 10 Ч Мd(<двоичное_число>);
Мd(<десятичное_число> `1') = 10 Ч Мd(<десятичное_число>) + 1;
Мd(<десятичное_число> '9') = 10 Ч Мd(<десятичное_число>) + 9.
Денотационную семантику программы можно определить в терминах изменений состояний идеального компьютера. Подобным образом определялись операционные ceмантики, приблизительно так же определяются и денотационные. Правда, для простоты они определяются только в терминах значений всех переменных, объявленных в программе. Операционная и денотационная семантики различаются тем, что изменения состояний в операционной семантике определяются запрограммированными алгоритмами, а в денотационной семантике они определяются строгими математическими функциями. Пусть состояние s программы определяется следующим набором упорядоченных пар: {<i1, v1>, <i2, v2>, …, <in, vn>}.
Каждый параметр i является именем переменной, а соответствующие параметры v являются текущими значениями данных переменных. Любой из параметров v может иметь специальное значение undef, указывающее, что связанная с ним величина в данный момент не определена.
Пусть VARMAP - функция двух параметров, имени переменной и состояния программы. Значение функции VARMAP(ik, s) равно vk (значение, соответствующее параметру ik в состоянии s).
Большинство семантических функций отображения для программ и программных конструкций отображают состояния в состояния. Эти изменения состояний используются для определения смысла программ и программных конструкций. Отметим, что такие языковые конструкции, как выражения, отображаются не в состояния, а в величины.
Выражения являются основой большинства языков программирования. Более того, мы имеем дело только с очень простыми выражениями. Единственными операторами являются операторы + и Ч; выражения могут содержать не более одного оператора; единственными операндами являются скалярные переменные и целочисленные литеральные константы; круглые скобки не используются; значение выражения является целым числом. Ниже следует описание этих выражений в форме БНФ:
<выражение> > <десятичное_число> | <переменная>
| <двоичное_выражение>
<двоичное_выражение> > <выражение_слева> <оператор>
<выражение_справа>
<оператор> > + | Ч
Единственной рассматриваемой нами ошибкой в выражениях является неопределенное значение переменной. Разумеется, могут появляться и другие ошибки, но большинство из них зависят от машины. Пусть Z - набор целых чисел, a error - ошибочное значение. Тогда множество Z {error} является множеством значений, для которых выражение может быть вычислено.
Ниже приведена функция отображения для данного выражения Е и состояния s. Символ є обозначает равенство по определению функции.
Me(<выражение>, s) є
case <выражение> of
<десятичное_число> => Md(<десятичное_число>, s)
<переменная> => if VARMAP(<переменная>, s) = undef
then error
else VARMAP(<переменная>, s)
<двоичное_выражение> =>
if (Me(<двоичное_выражение>.<выражение_слева>, s) = undef
OR Me(<двоичное_выражение>.< выражение_справа >, s) = undef)
else if (<двоичное_выражение>.< оператор > = '+' then
Me(<двоичное_выражение>.<выражение_слева>, s) +
Me(<двоичное_выражение>.<выражение_справа>, s)
else Me(<двоичное_выражение>.<выражение_слева>, s) Ч
Me(<двоичное_выражение>.<выражение_справа>, s).
Оператор присваивания - это вычисление выражения плюс присваивание его значения переменной, находящейся в левой части. Сказанное можно описать следующей функцией:
Ма(х = Е, s) є if Me(E, s) = error
then error
else s' = {<i1', v1'>, <i2', v2'>,..., <in', vn'>} where
for j = 1, 2, ..., n, vj' = VARMAP(ij', s) if ij <>x;
Me(E, s) if ij = x.
Отметим, что два сравнения, выполняющиеся в двух последних строках (ij <> x и ij = х), относятся к именам, а не значениям. После определения полной системы для заданного языка ее можно использовать для определения смысла полных программ этого языка. Это создает основу для очень строгого способа мышления в программировании. Денотационная семантика может использоваться для разработки языка. Операторы, описать которые с помощью денотационной семантики трудно, могут оказаться сложными и для понимания пользователями языка, и тогда разработчику следует подумать об альтернативной конструкции. С одной стороны, денотационные описания очень сложны, с другой - они дают великолепный метод краткого описания языка.
7.Декларативная семантика
Декларативная семантика является существенной характеристикой языков логического программирования, в которых программы состоят из объявлений (деклараций), а не из операторов присваивания и управляющих операторов. Эти объявления в действительности являются операторами, или высказываниями, в символьной логике.
Основная концепция декларативной семантики заключается в том, что существует простой способ определения смысла каждого оператора, и он не зависит от того, как именно этот оператор используется для решения задачи. Декларативная семантика значительно проще, чем семантика императивных (применяющихся в компьютерах с фон-неймановской архитектурой) языков. Она не требует для проверки отдельного оператора рассмотрения его контекста, локальных объявлений или последовательности выполнения программы.
Формальное определение семантики становится общепринятой частью определения нового языка. Стандартное описание языка PL/I включает в себя VDL-подобную нотацию, описывающую семантику операторов PL/I, а для языка Ada было разработано определение на основе денотационной семантики. Тем не менее, изучение формальных определений семантики не оказало такого сильного влияния на практическое определение языков, как изучение формальных грамматик -- на определение синтаксиса. Ни один из методов определения семантики не оказался по настоящему полезным ни для пользователя, ни для разработчиков компиляторов языков программирования.
8.Языки формальной спецификации
Языки и методы формальной спецификации, как средство проектирования и анализа программного обеспечения появились более сорока лет назад. За это время было немало попыток разработать как универсальные, так и специализированные языки формальных спецификаций (ЯФС), которые могли бы стать практическим инструментом разработки программ, таким же, как, например, языки программирования. За свою недолгую историю ЯФС уже прошли через несколько периодов подъема и спада. Оценки перспектив ЯФС варьировались от оптимистических прогнозов, предсказывающих появление языков описания требований, по которым будет генерироваться готовое программное обеспечение, до пессимистических утверждений, что ЯФС всегда будут только игрушкой для экспериментов в академических исследованиях. Вместе с тем, хотя сейчас даже термин «языки формальных спецификаций» известен далеко не всем программистам, нужно констатировать, что в этой отрасли программирования получены значительные результаты. Они выражаются, во-первых, в том, что есть несколько ЯФС, которые уже нельзя назвать экспериментальными, более того, некоторые ЯФС подкреплены соответствующими стандартами. Во-вторых, более четким стало представление о способах использования ЯФС, о месте формальных методов в жизненном цикле разработки программного обеспечения и в процессах его разработки и использования.
Второй из перечисленных факторов важен, поскольку способ использования языка, как, оказалось, серьезно влияет на эволюцию языка. Если рассматривать историю языков спецификации общего назначения (универсальных, не специализированных), то видно, что они развивались в соответствии со следующим представлением о «правильном порядке» разработки программного обеспечения:
? описать эскизную модель (функциональности, поведения);
? доказать, что модель корректна (не противоречива, консистентна);
? детализировать (уточнить) модель;
? доказать, что детализация проведена корректно;
? повторять два предыдущих шага до тех пор, пока не будет получена готовая программа.
Установка на данную схему процесса разработки приводила к акцентированному вниманию на средства обобщенного описания функциональности, средства уточнения, средства аналитического доказательства корректности в стиле традиционных математических доказательств. Как следствие, появились языки, которые не содержали в себе конструкций, затрудняющих аналитические доказательства, при этом они лишались и тех конструкций, без которых трудно описать реализацию сколько-нибудь сложной программной системы.
У специализированных языков несколько другая история. Некоторые из них, например SDL (Specification and Design Language), родились из практики проектирования систем релейного управления, где проект традиционно был больше похож на чертеж, чем на текстовое (языковое) описание. Здесь эволюция заключалась во взаимном сближении графической и текстовой нотации на основе взаимных компромиссов и ограничений. При этом участниками компромисса была некоторая графическая нотация и языки программирования, опыт, накопленный в других языках формальных спецификаций, либо игнорировался, либо заимствовался с большим опозданием.
ЯФС традиционно рассматривались как средство проектирования. Новый взгляд на ЯФС появился, когда стала актуальной задача анализа уже существующего программного обеспечения. Существенное продвижение на этом фронте было связано с направлением Объектно-Ориентированного Анализа. Его идеи во многом созвучны с Объектно-Ориентированным Проектированием. Не удивительно, что оба эти направления предлагают близкие изобразительные средства для описания архитектуры и поведения систем. В последнее время наиболее известным средством такого рода является (преимущественно) графический язык UML (Unified Modelling Language). Заметим, что UML и подобные ему языки спецификации, безусловно, являются неплохими средствами проектирования, но обычно непригодны для доказательства правильности, на что делался акцент в классических языках спецификации.
Совершенно новые требования к языкам спецификации появились с идеей использования их как источников для генерации тестов. Оказалось, что разные виды приложений требуют различных подходов к спецификации и имеют непохожие друг на друга возможности для генерации тестов. В частности, одни виды спецификаций в большей степени пригодны для генерации последовательностей тестовых воздействий (цепочек), тогда как другие предоставляют удобные возможности для генерации тестовых оракулов - программ, оценивающих результат, полученный в ответ на тестовое воздействие.
Имеется несколько способов классификации подходов к спецификации. Различают моделе-ориентированные и свойство-ориентированные спецификации или спецификации, основанные на описании состояний и действий. Единой классификации не существует, мы рассмотрим следующие четыре класса подходов к спецификации: исполняемые, алгебраические, сценарные и ограничения.
Исполнимые спецификации, исполнимые модели. Этот подход предполагает разработку прототипов (моделей) систем для демонстрации возможности достижения поставленной цели и проведения экспериментов при частичной реализации функциональности. Примерами таких методологий и языков являются SDL, RSL (RAISE Specification Language).
Алгебраические спецификации предполагают описание свойств композиций операций. Композиции могут быть последовательными, параллельными, с временными ограничениями и без. Преимуществом этого подхода является то, что в идеале можно полностью абстрагироваться от структур данных, которые используются в качестве входных и выходных значений и, возможно, используются для сохранения внутреннего состояния моделей. Основной недостаток - это нетрадиционность приемов спецификации, что затрудняет их внедрение в промышленных разработках. В качестве примера языка алгебраических спецификаций можно назвать ASN1 (Abstract Syntax Notation One), стандарт которого входит в группу стандартов, описывающих SDL, RSL.
Сценарные спецификации описывают не непосредственно целевую систему, а способы ее использования или взаимодействия с ней. Оказывается, что такие косвенные описания, с одной стороны, позволяют судить о некоторых свойствах системы (тем самым, они, конечно, являются спецификациями), и, с другой стороны, такие спецификации могут служить хорошим руководством по использованию системы, что не всегда можно сказать о других видах спецификаций. Наибольшее распространение получили работы OMG группы и продукты компании Rational Corporation.
Ограничения состоят из пред- и постусловий функций, процедур и других операций и инвариантов данных. Имеются расширения этого подхода для объектно-ориентированных спецификаций. В этом случае к спецификациям операций добавляются спецификации методов классов, а к инвариантам - инварианты объектов и классов. Языком, поддерживающим спецификацию ограничений, является RSL.
9.Методы доказательства правильности программ
денотационный семантика программирование
Как известно, универсальные вычислительные машины могут быть запрограммированы для решения самых разнородных задач - в этом заключается одна из основных их особенностей, имеющая огромную практическую ценность. Один и тот же компьютер, в зависимости от того, какая программа находится у него в памяти, способен осуществлять арифметические вычисления, доказывать теоремы и редактировать тексты, управлять ходом эксперимента и создавать проект автомобиля будущего, играть в шахматы и обучать иностранному языку. Однако успешное решение всех этих и многих других задач возможно лишь при том условии, что компьютерные программы не содержат ошибок, которые способны привести к неверным результатам.
Можно сказать, что требование отсутствия ошибок в программном обеспечении совершенно естественно и не нуждается в обосновании. Но как убедиться в том, что ошибки, в самом деле, отсутствуют? Вопрос не так прост, как может показаться на первый взгляд.
К неформальным методам доказательства правильности программ относят отладку и тестирование, которые являются необходимой составляющей на всех этапах процесса программирования, хотя и не решают полностью проблемы правильности. Существенные ошибки легко найти, если использовать соответствующие приемы отладки (контрольные распечатки, трассировки).
Тестирование - процесс выполнения программы с намерением найти ошибку, а не подтвердить правильность программы. Суть его сводится к следующему. Подлежащую проверке программу неоднократно запускают с теми входными данными, относительно которых результат известен заранее. Затем сравнивают полученный машиной результат с ожидаемым. Если во всех случаях тестирования налицо совпадение этих результатов, появляется некоторая уверенность в том, что и последующие вычисления не приведут к ошибочному итогу, т.е. что исходная программа работает правильно.
Мы уже обсуждали понятие правильности программы с точки зрения отсутствия в ней ошибок. С интуитивной точки зрения программа будет правильной, если в результате ее выполнения будет достигнут результат, с целью получения которого и была написана программа. Сам по себе факт безаварийного завершения программы еще ни о чем не говорит: вполне возможно, что программа в действительности делает совсем не то, что было задумано. Ошибки такого рода могут возникать по различным причинам.
В дальнейшем мы будем предполагать, что обсуждаемые программы не содержат синтаксических ошибок, поэтому при обосновании их правильности внимание будет обращаться только на содержательную сторону дела, связанную с вопросом о том, достигается ли при помощи данной программы данная конкретная цель. Целью можно считать поиск решения поставленной задачи, а программу рассматривать как способ ее решения. Программа будет правильной, если она решит сформулированную задачу.
Подобные документы
Знакомство с наиболее известными технологиями программирования. Особенности разработки программ для вычисления интеграла по формуле средних прямоугольников. Общая характеристика методов структурного программирования. Рассмотрение формулы Симпсона.
курсовая работа [1,3 M], добавлен 03.03.2015Характеристика предприятия ТОО "Com Sales Group". Составление программ на языке программирования. Составление алгоритмов, разработка численных методов решения задач. Методы откладки программ. Анализ технологии машинной обработки экономической информации.
отчет по практике [1,3 M], добавлен 19.04.2016Изучение особенностей операционной системы, набора программ, контролирующих работу прикладных программ и системных приложений. Описания архитектуры и программного обеспечения современных операционных систем. Достоинства языка программирования Ассемблер.
презентация [1,3 M], добавлен 22.04.2014Модели параллельного программирования; отладка параллельных программ. Реализация экспериментальной версии системы сравнительной отладки Fortran-OpenMP программ: получение, сбор и запись трассы, инструментарий программ, используемый формат файлов трассы.
дипломная работа [92,8 K], добавлен 17.10.2013Особенности способов описания языков программирования. Язык программирования как способ записи программ на ЭВМ в понятной для компьютера форме. Характеристика языка Паскаль, анализ стандартных его функций. Анализ примеров записи арифметических выражений.
курсовая работа [292,0 K], добавлен 18.03.2013Рассмотрение основ разработки технического задания. Проектирования структуры программ; описание соответственного алгоритма. Собственно программирование. Тестирование и отладка компьютерных программ. Ознакомление с основными правилами защиты проекта.
реферат [157,4 K], добавлен 15.11.2014Стандартизированный процедурный язык программирования. Создание системного программного обеспечения и прикладных программ. Особенности языка Си, его основные недостатки. Передача параметров в функцию по значению. Стандартная библиотека языка Си.
презентация [396,3 K], добавлен 12.11.2012Приемы работы с инструментальной средой программирования С++. Кодирование арифметических и логических выражений с использованием стандартных библиотечных функций ввода, вывода в С++. Описание переменной вещественного типа в языке программирования С++.
лабораторная работа [137,9 K], добавлен 13.06.2014Развитие интегрированных пакетов прикладных программ, механизмы, такие, как OLE и OpenDoc, обеспечивающие их совместную работу. Анализ наиболее известных комплексов, состоящих из прикладных программ, работающих как самостоятельно, так и интегрированно.
реферат [24,2 K], добавлен 03.03.2012Методика разработки внешних спецификаций программ, основанных на использовании HIPO-технологии проектирования программ. Приобретение практических навыков определения и оформления внешних спецификаций программ. Схема состава разложения и IPO-диаграммы.
лабораторная работа [45,6 K], добавлен 15.03.2009