- •10. Написание формальных спецификаций
- •Глава II
- •12. Предварительные замечания о процессе разработки программ
- •12.1. Жизненный цикл математического обеспечения
- •12. Предварительные замечания о процессе разработки программ
- •12.1. Жизненный цикл математического обеспечения
- •12.2. Анализ требований
- •12.3. Пример задачи
- •13.1. Обзор процесса проектирования
- •13.2.1. Вводный раздел
- •13.2.2. Разделы абстракций
- •13.8. Абстракция строки
- •13.9. Обзор и обсуждение
- •14. Этап перехода от проектирования к реализации
- •14.1. Оценка проекта
- •14.1.1. Корректность и эффективность
- •14.1.2. Структура
- •15.2. Выбор подхода
15.2. Выбор подхода
В процессе адаптирования языка программирования к методологии (и обратно) в качестве руководства могут быть использованы описанные в последнем разделе и гл. 7 подходы для языка Паскаль. Очевидно, что выбранный подход будет сильно зависеть от языка. Желательно везде, где это возможно, эффективно использовать особенности выбранного языка.
Например, в языке Ада имеется хорошая поддержка для абстракций данных, исключений и полиморфных абстракций. Для этого языка мы продвигались бы в следующем направлении:
1. Использование функций и процедур и определение их интерфейсов в терминах механизмов вызова языка Ада (in, out и inout).
2. Использование исключительных ситуаций языка Ада с соглашением, что пользователи имеют доступ ко всем приведенным в интерфейсе исключительным ситуациям плюс failure и явная сигнализация о возникновении ситуации failure во всех местах, где ее возникновение регистрировалось бы в языке CLU неявно (или явно). Такое соглашение необходимо по той причине, что язык Ада распространяет сигнал о необработанных исключительных ситуациях автоматически. В отсутствие такого соглашения нет способа гарантировать, чтобы процедура сигнализировала только о ситуациях, перечисленных в ее интерфейсе.
3. Реализация абстракций данных с пакетами и приватными или ограниченными приватными типами.
4. Замена итераторов генераторами. Генератор для типа реализуется в том же пакете, который реализует тип.
5. Реализация родовых параметров.
^6. Ассемблирование программ с помощью программного окружения языка Ада.
В языке Ада, как и в языке Паскаль, мы должны выбрать для имеющихся абстракций способ организации пространства для хранения их объектов — стек или неупорядоченный массив. Абстракция должна использовать неупорядоченный массив в том случае, если размеры объектов динамически изменяются. В противном случае можно использовать стек.
Типы, использующие неупорядоченный массив, существенно отличаются от типов, использующих стек. Тип, использующий стек, имеет операции создания, а его операции используют передачу •аргументов по значению. Он может также иметь операцию разрушения, если используемый язык не имеет механизмов сборки мусора (случай с языком Паскаль). Эта операция не требуется в языках, обеспечивающих такую поддержку (в случае использования языка Ада). Тип, использующий стек, не имеет этих операций, однако имеет операции инициализации, которые используют
Использование других языков
передачу аргументов по ссылке (или, в случае языка Ада, через значение/результат). (Операции инициализации не требуются, если язык допускает наличие у типа параметров, а также допускает использование этих параметров для инициализации нового распределенного пространства. Язык Ада имеет с этой точки зрения ограниченные возможности.) Необходимо отметить различие для этих случаев операции присваивания. Для типа, использующего стек, операция присваивания приводит к созданию копии, а для типа, использующего неупорядоченный массив, она вызывает разделение объектов. Генераторы стекоориентированных типов также отличаются от генераторов типов, использующих неупорядоченные массивы.
Решение о том, какой тип использует неупорядоченный массив и какой стек, принимается при их создании и отражается в спецификации, которая должна явно сообщать, какой из подходов предполагается. Реализация типа должна обеспечивать распределение пространства из стека или неупорядоченного массива в строгом соответствии со спецификацией. Использование метода, отличного от указанного в спецификации, является ошибкой.
Различие между этими двумя видами типов имеется также и для параметризованных типов. Параметризованный набор может содержать объекты, передаваемые в качестве аргументов, если э1и объекты находятся в неупорядоченном массиве. Однако в большинстве языков набор может состоягь из копий объектов, если они находятся в стеке. Это объясняется тем, что большинство языков не поддерживают явных указателей на позиции в стеке (исключением является Алгол 68). Если набор содержит фактические объекты, то при доступе к ним из этого набора изменения в объектах будут видны. Если набор содержит копии, то изменения видны не будут.
Поскольку данное различие весьма существенно, невозможно определить тип, который мог бы использовать в качестве параметра либо стекоориентированный тип, либо тип на базе неупорядоченного массива. Вместо этого параметризованный тип должен явно указывать в предложении requires, какой вид типа использован в качестве параметра.
Пример спецификации параметризованного типа приведен на рис. 15.5. Соглашения об именах соответствуют введенным в гл. 7. Подстановка делается при помощи редактора текста. В данном случае параметризованный тип представляет собой неограниченную очередь, следовательно, ее объекты находятся в неупорядоченном массиве. Однако предполагается, что тип параметра является стекоориентированным, что констатировалось в предложении requires. Отметим, что, поскольку тип параметра является стекоориентированным, аргументы этого типа передаются операциям над очередью по ссылке.
II Лисков Б., Гатэг Дж.
326 Глава 15
Использование других языков
queue == data type letype: type] is queue tetype].create, queue [etypel.enq,
queue [etypeLdeq,...
Requires etype принадлежит к стекоориентированному типу. Описание
queue [etype} принадлежит к типу, представляемому неупорядоченным массивом, который обеспечивает доступ к своим элементам по принципу «первый размещенный первым удаляется».
Операции
function queue [etype]_create(): queue [etype} effects Возвращает новую пустую очередь.
procedure queue [etype ]_enq (q: queue [etype}; var e: etype) modifies s effects Помещает копию e в очередь q в качестве последнего элемента,
procedure queue [etype]_deq (q: queue [etype]; var e: etype)
requires Очередь q не пуста. modifies q effects Удаляет первый элемент из q и копирует его в e.
end queue (type] Рис. 15,5. Параметризованный тип.
В зависимости от того, как организованы типы, для их реализации в большинстве языков необходимо вводить некоторые соглашения. При разработке этих соглашений необходимо соблюдать одно главное требование:изменения в типе не должны предполагать модификацию исходных текстов любых модулей, использующих данный тип.Оба метода, показанные для языка Паскаль, удовлетворяют этому требованию.
Имеется также ряд других критериев. Во-первых, необходимо, чтобы выбранный метод и соглашения можно было легкоиспользовать.Легкость использования метода не только повышает вероятность его применения, но и сокращает схожесть ошибок, возникающих при его некорректном использовании. Для достижения этой цели может возникнуть необходимость в некоторой поддержке, например в макроопределениях или препроцессоре.
Вторым важным критерием являетсямодифицируемость.Если это возможно, изменения в реализации абстракции данных не должны требовать повторной компиляции каких-либо модулей. Хотя нарушение этого критерия не слишком критично, при его отсутствии объем повторной компиляции может оказаться весьма значительным и сделать сопровождение системы тяжелым и дорогостоящим. Более того, если программисты должны помнить, какие модули необходимо повторно компилировать, то использовать метод будет трудно, а вероятность появления ошибок возрастет. Существуют программные средства, автоматически определяющие модули, подлежащие перекомпилированию.
Желательно также соблюдение двух других критериев — надежности типа и защищенности,однако для некоторых языков это требование труднодостижимо. Под надежностью типа понимается поддержание четкого различия между объектами, принадлежащими к разным типам: объект с одним типом никогда не •может быть использован в контексте, в котором требуется другой тип. Например, не должно существовать возможности передачи полинома операции установки. Язык СШ поддерживает такую защищенность, осуществляя проверку на этапе компиляции, это же обеспечивают языки Паскаль и Ада. В других языках проверка на этапе компиляции может оказаться невозможной. Например, в языке ПЛ/1 объекты типа, использующего неупорядоченный массив, могут быть представлены нетипизованными указателями, т. e.тип используемого хранилища не отражен в типе указателя. Например, тип дляintsetможет быть простоptr. Поскольку тип каждого абстрактного объекта, расположенного в неупорядоченном массиве, представляется одним и тем же, то нарушения надежности типа не могут быть обнаружены компилятором языка ПЛ/1. Лучшее, что можно сделать,—это ввести проверку на этапе выполнения, однако это может оказаться довольно обременительным.
Защищенностьпредполагает, что доступ к представлению объекта возможен только изнутри реализации типа этого объекта. ЯзыкCLUподдерживает защиту, ограничивая использование операцийup,downиcvtкластерами, а определения их —как обеспечивающих преобразования между абстракцией данных кластера и ее представлением. Язык Ада поддерживает защиту в том случае, если абстракции данных определены с использованием приватных или ограниченных приватных типов. Отметим, что при отсутствии защиты невозможно гарантировать локальное управление и в этом случае его корректность не может рассматриваться локально. К сожалению, если язык не поддерживает механизм инкапсуляции, то защита является труднодостижимой целью. Если это так, то лучше использовать другие методы обеспечения защиты, например чтение программы различными людьми.
15.3. Заключение
При использовании нашего ^лeтодaпрограммирования с каким-нибудь языком, отличным от языка CLU,возможны ситуации, в которых язык не слишком хорошо вписывается в предложенный метод. В таком случае мы можем предложить следующее:
1.Измените методологию языка, например, изменением формы процедурных абстракций.
2.Введите соглашения, касающиеся реализаций абстракций в языке.
328 Глава 15
Изменением методологии языка мы гарантируем тот факт, что созданные нами программы станут на нем реализуемы. Соглашения обеспечат легкость и однозначность в реализации.
Выбор подходящего языка программирования чрезвычайно важен. Некоторые языки обеспечивают лучшую модульность проекта и реализации, чем другие. Методология более важна, чем язык программирования, хотя она и может быть использована с .произвольным языком. Возможносги, предоставляемые моду-ляризованностью и абстракциями, обеспечиваются используемым методом, а не языком программирования.
Дополнительная литература
Shaw, Mary, 1984. Abstraction techniques in modern programming languages. IEEE Software 1 (4): 10—26.
Упражнения
15.1. Опишите и реализуйте на языке Паскаль ограниченную FIFO очередь емкостью в 100 элементов. Не забудьте создать генератор для итерации по элементам в очереди. Используйте стекоориентированную реализацию.
15.2. Определите использующий данную методологию способ работы с языком, отличным от языка Паскаль и CLU. Создайте образец программы для каждой части метода. Не забудьте включить итераторы, использующие неупорядоченный массив типы данных, исключительные ситуации и стекоориентированные типы данных.
15.3. Рассмотрите относительные преимущества и недостатки стекоориенти рованного и использующего неупорядоченный массив подхода к реализации абстракций данных.
Приложение А Справочное руководство по языку CLU
Университеты могут получить реализацию языка CLU у авторов за номинальную стоимость. В настоящий момент имеются реализации под операционной системой UNIX на ЭВМ DEC Vax и Motorola 68 000.
.1. Синтаксис
Для определения ситаксиса мы воспользуемся бекусовской нормальной формой грамматики. Общая форма продукции имеет вид:
нетерминальный : := альтернатива ) альтернатива 1 ... 1 альтернатива Используются следующие расширения:
а, ... список одного или более вхождений а, разделенных запятыми: «а» или
«а, а» или «а, а, а» и т. д.
{а) последовательность из нуля или более вхождений а: «» или «а» или «а а»
и т. д.
[а] необязательное вхождение а: «» или «а».
Нетерминальные символы набраны обычным шрифтом. Зарезервированные слова набраны жирным шрифтом. Все остальные терминальные символы иеалфа-витные и набраны обычным шрифтом:
модуль : := {равенство} процедура 1 {равенство} итератор 1 {равенство} кластер
процедура : := идент-р == ргос [параметры] аргументы [returns] [signals]
[where ]
тело. процедуры end идент-р
итератор : :== идент-р = iter [параметры] аргументы [yields] [signals] [where]
тело- процедуры end идент-р
кластер '. := идент-р = cluster [параметры] is идент-р, ... [where]
тело- процедуры end идент-р параметры ; :== [параметр, .,.]
параметр : :== идент-р, ...: type \ идент-р, ...: специф-типа аргументы : :== ([объявл, ...] объявл : :== идент-р, ...: специф-типа returns : := returns (специф-типа, ...) yields '. '.= yields (специф-типа, ...) signals : :== signals (искл-ситуация, ...)
Приложение В
Набор заданий по программированию
Данное приложение содержит пять задач по программированию, подобранных в качестве заданий в нашем односеместровом лабораторном курсе.
Задачи 1 и 2 имеют дело с деревьями и графами. Первая из них выдается в качестве задания на первой неделе семестра. На ее выполнение отводится неделя. Это предполагает написание нескольких относительно несложных программ, использующих абстракции данных, и имеет цель проиллюстрировать полезные абстракции. На вторую задачу также отводится одна неделя. В процессе ее решения студенты реализуют абстракции, использованные в первой задаче.
В основе задачи 3 лежит описанный в гл. 13 форматировщик. Студентам выдается реализация, приведенная в приложении Б, и дается три недели на выполнение набора модификаций. Эта задача предназначена для анализа проблем, возникающих в процессе модификации программ.
Задача 4 отличается от предыдущих своим значительным объемом. Студентам дается 3 недели на разработку и реализацию интерактивного корректировщика правописания. На первой стадии предполагается уточнение намеренно расплывчатой спецификации требований и разработка реализации. На второй стадии студенты реализуют проект.
На пятую задачу отводится месяц. Группам из двух или трех студентов поручается разработка и реализация программы, сходной с программой VisiCalc^. Этот проект состоит из нескольких промежуточных стадий, рассмотренных ниже.
Задача 1: использование абстракций данных
В этой задаче вы должны будете написать процедуры для просмотра графов и деревьев. Процедуры должны работать с ориентированными графами, деревьями, наборами целых чисел и очередями. Эти абстракции данных описаны ниже. Там же приводятся и их реализации.
Ориентированный граф (орграф) представляет собой набор узлов и набор ребер. Ребро представляет собой ориентированную пару узлов. Если ребро {по, tii} принадлежит ориентированному графу g, то мы говорим, что Hi является последователем По в графе g.
Путем из узла По в узел Пщ называется последовательность длиною более 1 узлов По, ..., Пщ, таких, что для каждого i == 0, ..., m —1 имеется ребро из щ к m+i. Мы говорим, что узел Пт достижим из узла по, если существует путь из По в Пщ. Узел может быть достижим или недостижим из самого себя.
Список смежных узлов узла п представляет собой список последователей для узла п. Представление списка смежных узлов ориентированного графа представляет собой набор списков смежности для каждого узла в g.
Список в ширину узлов в ориентированном графе g состоит из узла п, за которым следует каждый последователь п, затем последователи этих последователей и т. д. Все узлы, достижимые из узла п, появляются д списке ровно один раз. В нашей задаче каждый узел ориентированного графа из п узлов пронумерован числами от 1 до п. Эти номера являются единственной возможностью ссылки к узлам.
Набор заданий по программированию
401
Дерево представляет собой ориентированный граф, удовлетворяющий следующим свойствам;
1. Дерево имеет отличительный узел, называемый корнем, который не является последователем ни для одного из узлов.
2. Каждый узел в дереве, отличный от корневого, является последователем в точности одного узла.
3. Деревья являются связными. Это означает, что все узлы в дереве достижимы из корневого узла. Деревья являются ацикличными. Это означает, что у узла отсутствует путь к самому себе.
Если ориентированный граф является деревом, то мы используем вместо слова «последователь» слово «дочерний» или «потомок».
Программа просмотра дерева
В первой части задачи вы должны написать процедуру, принимающую на входе дерево и номер (метку) узла и печатающую список в ширину данного дерева. Спецификация данной процедуры есть
breadth- first, tree =proc(t: tree, n: int) requires n есть метка узла в дереве t. modifies первичный ввод.
effects Печатает (для первичного входного потока) список в ширину для дерева t, начиная с п.
Вам будут предоставлены реализации абстракций данных дерева и очереди Эти абстракции приведены на рис. B.I и В.2. Абстракция очереди удобна для внутреннего контролирования процесса просмотра дерева.
tree== data type is create, add. node. ancLedge, children, put Описание
Дерево представляет собой ациклический связный ориентированный граф с отличительным 'коряевь^и. узлом. Деревья неизменяемы.
Операции
create == proc ( ) returns (tree)
effects Создает новое дерево, содержащее один узел, помеченный целым числом 1.
add_ node. and_ edge == proc (t: tree, n: int) returns tree, int) requires n есть метка узла в t.
effects Создает новое дерево, содержащее узлы и ребра графа t, плюс новый узел и новое ребро от п к этому узлу. Возвращает новое дерево и новую уникальную целочисленную метку для нового узла. Отметим, что граф t не модифицируется.
children = proc (t: tree, n: int) returns (sequence (Int)) requires n есть метка узла в t.
effects Возвращает последовательность, содержащую метку каждого потомка узла п. :
put == proc (t: tree, si stream) modifies s effects Выводит представление списка смежных узлов дерева Т в поток а.
end tree Рис. B.I. Спецификация типа данных tree.
402 Приложение В
queue = data type is create, enq, deq, is-empty Описаике
Очередь queue хранит целые числа по принципу «первый поступивший первым удаляется».
Операции
create = proc ( ) returns (queue) effects Создает и возвращает новую пустую очередь,
enq = proc (q: queue, n: int)
modifies q effects Добавляет n к концу очереди q.
deq = proc (q; queue) returns (int) requires Очередь q не пуста. modifies q effects Удаляет целое число из начала очереди q и возвращает это число.
is_ empty = proc (q; queue) returns (bool) effects Возвращает значение true, если очередь q пуста: в противном случае возвращает значение false.
end queue Рис. В.2. Спецификация типа данных queue.
Просмотр графа
Вы должны lenepb модифицировать копию процедуры breadth-first-tree для удовлетворения следующих спецификаций:
breadtti_first-graph=proc(g: digraph, n: int) requires n есть метка узла в диграфе g. modifies первичный вывод.
effects Печатает (для первичного входного потока) список в ширину для графа g, начиная с n.
Мы приведем реализации абстракций данных intset и digraph. Спецификация digraph приведена на рис. В.3. Наборы intset приведены на рис. 4.2. Они могут быть полезны для контроля за тем, чтобы каждый узел просматривался в точности один раз.
Задача 2: написание абстракций данных
Ваша задача теперь заключается в том, чтобы реализовать абстракции tree, queue и digraph, рассмотренные в задаче 1. Не забудьте включить в реализации инварианты представления и функции абстракции.
Каждая из этих абстракций имеет операции с предложениями requires. Эти предложения должны быть удалены и заменены соответствующими исключительными ситуациями. Приведите также эти исправленные спецификации. (Реализация исправленной спецификации для набора intset 'приведена на рис. 5.4.) Ваше решение должно включать также описание тестов. Тестировгние должно быть в состоянии показать, что ваши решения задачи 1 работают с ва-кми кластерами. Однако это не является достаточным основанием для утвержде-кя icio, что кластеры отмечают требуемой спецификации.
Набор заданий по программированию
digraph ^ data type is create, add. node, add.edge, add-node_and-edge, successors, put
Описание Диграфы принадлежат к неизменяемым ориентированным графам.
Операции.
create == proc ( ) returns (digraph)
effects Создает новый диграф, содержащий один узел. Узел помечается целым числом 1.
add. node = proc (g: digraph) returns (digraph, int) effects Создает новый диграф, содержащий узлы и ребра графа g и один дополнительный узел. Возвращает новый граф и уникальную целочисленную метку для этого узла. Отметим, что граф g не модифицируется.
add-edge= proc (g: digraph, initial, final: int) returns (digraph)
requires initial и final есть метки узлов в g.
effects Создает иовый диграф, содержащий узлы и ребра графа g, и ребро от initial к final. Возвращает новый граф. Если ребро уже существует, то новый граф неотличим от графа g. Отметим, что в обоих случаях граф g не изменяется.
add-node-and. edge == proc (g: digraph, n; int) returns (digraph, int)
requires n есть метка узла в g.
effects Создает новый диграф, содержащий узлы и ребра графа g, плюс новый узел и новое ребро от n к данному узлу. Возвращает новый граф и новую целочисленную метку для данного узла. Отметим, что граф g не изменяется.
successors == proc (g: digraph, n: int) returns (digraph, int)
requires n есть метка узла в g.
effects Возвращает последовательность дочерних узлов для узла n в графе g.
put = proc (g: digraph, s: stream)
modifies s
effects Выводит представление списка смежных узлов диграфа g в поток s.
end digraph Рис. В.3. Абстракция graph.
Задача 3: модификация форматировщика текста
Эта задача предназначена для выработки у вас навыков в модификации программного обеспечения, созданного другим человеком. Вам необходимо будет ознакомиться с исходной программой и ее документацией, а затем модифицировать эти документы. В качестве модифицируемой программы в данном случае выступает форматировщик текста, описанный в приложении Б. Вы должны выполнить следующие модификации форматировщика;
1. Измените размер левого края на 12 пробелов. Левый край есть числи пробелов между левым краем страницы и первой позицией текста.
2. Добавьте команды .line width [+/—] N и page-lehgth [+/—] N, модифицирующие соответственно ширину строки и число строк на странице следующим образом: если целочисленному аргументу n предшествует знак плюс (или минус), то он добавляется (или вычитается) к предыдущему значению, давая ровое значение. В противном случае новое значение становится аргументом N. (Начальные
404 Приложение В
значения ширины строки и размера страницы есть 60 и 50 соответственно.) Команда .page. length оказывает эффект, начиная со страницы, следующей за текущей, если только текущая страница не пуста. Если текущая страница пуста, то команда выполняется немедленно.
3. Добавьте команду .title «текст», задающую заголовок текста. Заголовок должен появляться на первой строке каждой страницы. Эта команда вызывает переход к новой строке.
4. Модифицируйте печать номеров страниц таким образом, чтобы номера появлялись в правом углу для страниц с нечетными номерами, и в левом углу — для страниц с четными номерами. Номер страницы не должен располагаться на одной строке с заголовком.
5. Добавьте команды .begin-quote N и .end-quote, которые используются для установки длинной цитаты, начиная с конца текста. Команда begin-quote N вызывает переход к следующей строке, выводит пустую строку и выполняет отступ с обоих краев на N пробелов (края становячея шире, а строки текста — короче). Команда end-quote вызывает переход к следующей строке, печать пустой строки и восстановление исходных значений для правого и левого края.
6. Добавьте команду .begin- paragragh, которая отмечает начало параграфа. Эта команда вызывает переход к новой строке. Затем, если на странице осталось менее трех пустых строк, выполняется переход к новой странице. Первая строка параграфа будет отстоять от левого края на пять пробелов.
Вы должны также выполнить в программе следующие изменения, однако не реализовывать их. Приведите спецификации и список программных изменений, который необходим для реализации следующих команд.
1. Добавьте команду .need N, которая заставляп форматировщик продолжать вывод на текущую страницу только в том случае, если на странице поместится число строк, заданное числом N. В противном случае команда вызовет переход к следующей странице. В обоих случаях команда вызывает переход к следующей строке.
2. Добавьте команды .begin-table и .end-table, использу^лые для отделения таблиц от остального текста. Все строки между командой .begin-table и командой end-table сохраняются для вывода на начало следующей страницы (или текущей страницы, если она пуста). Если текущая страница непуста, то вывод продолжается, как если бы вся информация, заключенная между командой .begin-table и командой end-table, отсутствовала. Наверху следующей страницы (или текущей, если она пуста) сохраненные строки форматируются и печатаются сразу же за заголовком. Затем печатается пустая строка, ряд дефисов и еще одна пустая строка, после чего продолжается вывод текста в обычном режиме.
Для решения этой задачи вам необходимо сделать в спецификациях два вида модификаций: модификации в спецификации требований и модификации в спецификациях модулей. Изменения в спецификациях модулей понадобятся при модификации программы. Изменения в спецификации требований также необходимы, поскольку приведенный выше список модификаций не учитывает специальные случаи И ошибочные ситуации. Вы должны дополнить спецификацию, решив, каким образом подобные случаи должны быть учтены. Например, для команд с числовым аргументом необходимо решить, что делать в тех случаях, когда аргумент пропущен или ошибочен. Не забывайте о принципе полноты: если вы захотите ввести стандартные значения, то сделайте это для всех команд. Также решите, как табличные команды влияют на форматируемое окружение. Ваше решение проблемы должно включать следующее:
1. Описание сделанных изменений в сп цификации чребований. Это должно быть краткое, но полное описание возможных ошибок пользователей и способов их обработки.
2. Описание изменений в спецификациях модулей. Сами спецификации модулей должны быть приведены в листингах модулей.
3. Документация всех изменений в проекте. Помимо обычной информации этот раздел должен включать анализ степени сложности каждого из изменений. Основ-
Набор заданий по программированию
ным признаком хорошего проектирования является степень легкости расширения и модификации результирующей программы. Анализируя степень трудности каждого изменения, вы получите представление о том, насколько проектирование и реализация программы влияют на легкость модифицирования.
4. Листинга всех модифицированных вами модулей и изменения должны быть выделены и снабжены комментариями. Вы не должны приводить листинга модулей, не подвергшихся изменениям.
5. Выходные данные тестовых проверок, демонстрирующие, что изменения были успешны. Приведите описание всех тестовых проверок.
6. Разработайте документацию всех изменений, которые предполагалесь спроектировать, но не реализовать.
Задача 4: интерактивный корректировщик правописания
Корректировщик правописания представляет собой программу, которая получает в качестве входных данных словарь и текстовый файл и исправляет затем в текстовом файле ошибки правописания. Программа работает, отыскивая каждое слово текстового файла в словаре. Если слово отсутствует в словаре, то оно может содержать или же не содержать ошибку. В обоих случаях программа отображает слово на экране с объемом окружающего текста, достаточным для того. чтобы показать контекст, в котором оно находится. Пользователь может предпринять несколько действий по исправлению ошибки: перепечатать неверно написанное слово, заставить программу ввести его без изменений и включить это слово в словарь. Результатом работы корректировщика является исправленный текст и, возможно, обновленный словарь.
Корректировщик правописания должен иметь достаточно эффективную реализацию по двум соображениям. Во-первых, программа выполняет большое количество вычислений, поэтому неэффективная реализация сделает ее непрактичной. Во-вторых, поскольку программа является интерактивной, работа пользователя за терминалом должна быть максимально облегчена. Основная проблема заключается в поиске слов в словаре. Эта операция выполняется для каждого слова во входном тексте. Для удовлетворения требования по эффективности нам необходим алгоритм просмотра, более быстрый, чем простой линейный поиск в словаре. Примеры алгоритмов просмотра с приемлемой эффективностью есть (1) бинарный поиск по упорядоченному списку, (2) просмотр упорядоченного бинарного дерева и (3) хэширование.
Мы будем ориентироваться на словарь из нескольких тысяч слов. Словарь представляет собой ASCII файл, слова в котором представлени в буквах нижнего регистра и разделены пробелами, символами табуляции н перевода строки. Слова в словаре неупорядоченны.
Этот набор требований к программе spell является неполным. Для словаря может понадобиться дополнительная информация. Вы должны также принять решения о возможностях, предоставляемых программой, и так же подробно проанализировать обработку входных символов. Можно вывести ряд предположений:
1. Символьная обработка. Вы должны определить то, каким образом программа обращается с символами пунктуации, например с кавычками, со словами или акронимами, включающими цифры, дефисами, стандартными сокращениями (« и т. д.») и так далее. Например, вы можете рассматривать дефисы как ограничители, поскольку большинство слов с дефисами являются сочетаниями из нескольких распознаваемых подчастей.
2. Интерфейс с пользователем. Интерфейс с пользователем в интерактивной программе является весьма важной частью, которая должна быть тщательно продумана. Необходимо, чтобы программой можно было легко пользоваться. На этот счет имеются следующие соображения: а) возложите на пользователя
406 Приложение В
указание имен для исходного и результирующего файлов — как для текста, так и для словаря; б) дайте возможность пользователю исправить несколько текст» вых файлов без выхода из программы корректировщика; в) запрашивайте у пользователя ввод возможных правильных ответов при отображении неверно заданного слова; г) реализуйте команду help («вспомогательная информация»), описывающую эффект каждого из вышеописанных ответов подробно.
Помимо рассмотренной базовой проверки вы можете попытаться реализовать несколько следующих расширений. Они не обязательны. Вы можете попытаться выбрать одну или обе из них только в том случае, если необходимая часть проекта выполнена достаточно хорошо.
1. Большинство программ коррекции правописания предполагает, что незнакомое слово написано неверно, и пытается угадать слово, отыскивая слова со сходными пропусками в словаре. Кандидаты в слова со сходным пропусками могут быть сгенерированы путем анализа большинства распространенных ошибок правописания. Буквы часто меняются местами (например, «hte»), слово может содержать лишнюю букву или на букву меньше, или одна из букв может быть неправильной. В этом случае вы имеете возможность реализовать стратегии для двух первых типов ошибок. Исправления пропущенных и неправильных букв требуют слишком серьезного анализа эффективности и в данном случае неоправданны.
2. Другим расширением программы корректировщика является программа автоматической корректировки. Большинство людей имеют набор слов, в которых они ошибаются постоянно. Описанная программа корректировки запрашивает пользова1еля каждый раз при обнаружении таких слов. Это довольно быстро становится утомительным. Имея описанное расширение, пользователь задает соответствующее правописание неправильно набранного слова только один раз и указывает, чтобы данная коррекция была зарегистрирована. С этого момента программа автоматически делает изменения, не обращаясь к пользователю.
Решение этой проблемы может быть разбито на две части. Первая часть представляет собой документ (половина проекта), описывающий проект вашей программы корректировщика правописания. Этот документ должен включать спецификации модулей, которые вы намереваетесь реализовать, а также исправления и коррекции в спецификации задачи, которые вы делаете на этапе проектирования. Вторая часть включает тексты программ, тестовые проверки а полную документацию корректировщика.
Задача 5: TriviCalc (месячный проект)
Предположим, что вы являетесь инженером объединения по разработке программного обеспеченгя фирмы Softfeet. Все существующее программное обеспечение компании написано на языке ассемблера. Однако дирекция компании большинством голосов приняла решение о переводе всего программного обеспечения на язык высокого уровня. В качестве такого языка был выбран язык CLU. При принятии решения дирекция компании руководствовалась также и вашей аргументацией.
Начальник вашего отдела вызвал вас к себе. Вы знаете, что он презрительно относится к интересу, проявленному к вам руководством фирмы, и решает поставить вас на место. Он дает вам задание переработки наиболее прибыльной программы компании — TriviCalc. Исходная реализация задачи TriviCalc заняла десять человеко-месяцев работы. Поскольку вы подчеркнули, что программное обеспечение, созданное на языке высокого уровня, гораздо легче создавать и отлаживать, то ваш шеф говорит вам, что он ожидает от вас в составе группы из еще одного или двух инженеров окончания работы над аналогичной программой на языке CLU в течение четырех недель.
Набор заданий по программированию
Оставшаяся часть данного раздела содержит информацию об этапах работы и сроках сдачи, справочное руководство по программе TriviCalc, некоторые расширения, которые необходимо рассмотреть, ряд советов, касающихся реализации, а также спецификацию абстракции данных, которая может быть использована при реализации.
Этапы сдачи задачи
Перед сдачей проекта вы должны подготовить два документа. Первый, на который отводится срок 9 дней, должен содержать замечания и дополнения н спецификации задачи TriviCalc и должен описывать предварительный проект TriviCalc. Каждая группа должна получить и рассмотреть этот документ. Проекты должны быть обсуждены в этот же день. При следующей классной встрече ваш ассистент должен вернуть ваш проект с замечаниями и предложениями по его улучшению. Затем вам дается девять дней на подготовку этого улучшенного проекта и нового документа. Исправленные проекты обсуждаются в классе Окончательная версия, включая тексты модулей, должна быть готова через девять дней после сдачи исправленного проекта. Оценка снижается, если окоича^льный проект значительно отличается от исходного исправленного.
После того как проект будет готов, каждая группа должна предоставить свою программу. Отчет о выполненном задании ограничен 25 мин на группу. В течение первых 20 мин группа должна продемонстрировать возможности программы. Последние 5 минут преподаватель тестирует программу.
Проектирование документации
Изначальный и откорректированный проекты должны быть как можно более полными. Они должны содержать следующую информацию: 1. Все исправления в справочном руководстве, включая список всех сообщений об ошибках. Убедитесь в том, что все исправления не отразятся на функциональности TriviCalc.
2. Диаграмму модульной зависимости.
3. Обзор проекта.
4. Спецификацию каждого модуля.
5. Реализацию. Напишите обзор вашей стратегии реализации, сфокусировав внимание на порядке реализации и тестирования модулей, а также на том, какие заглушки и драйверы используются. Будьте готовы обсудить как индивидуальное, так и интегральное тестирование. Выделите, какие модули могут разрабатываться и тестироваться параллельно.
6. Список интегральных тестировочных проверок. Опишите и обоснуйте список проверок, которые вы будет использовать для того, чтобы убедить вашего ассистента в правильности работы программы. Постарайтесь сделать эти проверки максимально краткими и полными,
7. План и распределение работы. Выделите достаточное время на завершение про кта. Укажите, кто будет реализовывать каждый модуль. Временной график работы должен иметь четко определенные этапы, включая завершение работы над реализацией, окончание разработки заглушек, используемых в дальнейшем другими членами вашей группы, завершение разработки некоторой группы операций или завершение индивидуального тестирования отдельного модуля.
Справочное руководство по программе TriviCalc
TriviCalc представляет собой программу, которая может быть использована в качестве черновика при решении проблем, связанных с арифметическими вычислениями. Пользователь программы вводит числа или текст в некоторый опре-
408 Приложение В
деленный участок памяти компьютера. Затем данные отображаются на экране дисплея. Пользователь может также комбинировать уже выведенные на экран значения для получения новых значений, которые сохраняются в компьютере и отображаются в каком-нибудь другом месте экрана. После этого программа запоминает взаимоотношения между числами, что позволяет веста вычисления с различными значениями.
В качестве примера представим себе два числа, отображаемые на экране с метками А1 и В1. Пользователь вводит команду, которая заставляет программу TriviCalc прибавить значение, хранимое в А1, к значению, хранимому в В1, и сохранить результат в С1. Если пользователь в дальнейшем изменит значение А1, то значение С1 также изменится, по-прежнему оставаясь при этом суммой А1 и В1.
Интерфейс с пользователем
В процессе работы с TriviCalc экран терминала делится на три части. На рис. В.4 приведена диаграмма состояния экрана после вызова программы.
Первая строка терминала, называемая строкой статуса состояния, используется для отображения описаний состояния команд программы. Эта строка разделена на две части: левая часть содержит сообщения об ошибках. Правая часть отображает информацию о содержимом текущей ячейки.
Вторая строка экрана терминала используется в качестве рабочей области. При вводе пользователем текстовых i-ли числовых данных они отображаются в этой строке, задерживая выполнение до момента подачи заданной пользователем команды. Отображаемый в этой области текст может быть отредактирован каким-нибудь простым редактором, подробно описанным в следующем разделе.
нцрсор. \ ^ TRWI
Поле сглягт/са. состояния Содерзкимое теку" \ щей ячейки \
TRIVICALC, версия ^5-1,97 PaSovee поле
19
20 •21
Текущая ячейка.
::i^rj
Лчелка. EQ
.Оёпаепп сохранения
Рис. В.4. Исходный формат экрана программы TriviCalc,
Набор заданий по программированию
Остаток экрана занят тем, что называется областью сохранения. Он используется для отображения текстуальных представлений всех значений, сохраняемых во внутренней памяти программы TriviCalc, Эта область поделена на поля или «ячейки» шириной в восемь символов и высотой в один. Каждая ячейка идентифицирована числом, задающим номер строки, и буквой, задающей номер столбца, начиная от верхнего левого угла области сохранения. Так, ячейка в этом углу имеет имя «А1», а противоположная ей по диагонали, в нижнем правом углу области сохранения (и экрана), называется «Н21». Слева и сверху области сохранения расположена граница, содержащая имя каждой строки и столбца.
В области сохранения всегда имеется одна ячейка, которая называется текущей. Эта ячейка отображается выделение, например в инверсном изображении, с тем чтобы легко распознаваться пользователем. Изначально такой ячейкой является А1. Для облегчения ее распознавания в различных областях содержимое текущей ячейки и строки заголовка рабочей области должны также быть выделены. (Инверсное изображение на рисунке не показано.)
Описание операций
Внутренняя память программы TriviCalc представляет собой двумерный массив, к элементам которого ссылаются через буквы в интервале А—Р по одному измерению и числа 1—12 — по другому. Текстовое представление содержимого каждого элемента отображается в области сохранения экрана терминала.
Элементы области внутренней памяти могут быть трех типов: (1) пустой влемент, (2) значение и (3) комментарий. Пустой элемент не имеет значения. Комментарий представляет собой строку длиной до восьми символов. Если комментарий занимает один элемент массива, то он не оказывает никакого эффекта на другой его элемент.
значение представляет собой число с плавающей запятой. Значения в памяти могут быть связаны друг с другом операциями умножения, деления, сложения и вычитания. Эти отношения устанавливаются пользователем и могут быть изменены в любой момент. Некоторые значения в памяти могут быть получены в результате выполнения вышеуказанных операций. Полученное значение зависит от других значений.
Имеются команды изменения содержимого элементов внутренней памяти. Причиной возможных ограничений, рассматриваемых ниже, является то, что содержимое такого элемента не обязано принадлежать к тому же типу, что и старое значение. При изменении некоторого значения зависящие от него значения элементов также могут измениться,
Имеется ряд общих правил, касающихся изменения области сохранения задачи TriviCalc. TriviCalc поддерживает непротиворечивость области сохранения. Имея вышеописанный набор команд, возможно устанавливать конфигурацию значений в памяти, которая не имеет непротиворечивой интерпретации. Интерпретатор команд задачи TriviCalc исключает возникновение такой ситуации. Если вводимая команда может привести к противоречивости в области сохранения, то команда не выполняется и высвечивается сообщение об ошибке. Правила, которыми руководствуется интерпретатор команд задачи TriviCalc, следующие:
1. Зависеть от других элементов области сохранения могут только те элементы, содержимое которых есть значение.
2. Если содержимое элемента зависит от других элементов, тип данного элемента не может быть изменен.
3. Если содержимое элемента зависит от тех или иных элементов, то изменение содержимого зависимого элемента приводит к нарушению зависимости.
4. Данный момент не может зависеть, даже косвенно, от своего собственного значения.
410 Приложение В
Процессор команд TriviCalc
Помимо описываемых ниже команд перемещения, все команды и их аргументы вводятся редактором в рабочую область. При выполнении команды вся рабочая область передается на обработку процессору команд TriviCalc, который либо сразу же выполняет эту команду, либо отбрасывает ее как ошибочную. Процессор команд TriviCalc никогда не модифицирует содержимое рабочей области на терминале.
Ниже приводится полное описание поддерживаемых командным пропессо-ром операций для каждой команды. Обозначение ) {символ] используется для указания клавиш, которые должны быть нажаты при предварительно нажатой клавише CONTROL.
SAVE; FILE: name; Текущее состояние области сохранения сохраняется в файле с именем name. tc, где name есть аргумент FILE. Состояние сохраняется в виде строки из ASCII символов, содержащей последовательность команд TriviCalc, разделенных символами перехода к новой строке. Эти команды должны обладать тем свойством, что при выполнении их в последовательности, начиная с пустой ячейки области хранения, они должны заполнить область хранения таким образом, чтобы она имела вид, соответствующий моменту сохранения.
LOAD; FILE: name; Текущее состояние области сохранения сбрасывается и перезагружается на основании информации, хранящейся в файле name.tc. Предполагается, что файл имеет формат, установленный командой SAVE.
STORE-COMMENT; WITH: string; AT: slot-address; Строка комментария
string сохраняется в элементе памяти с меткой slot-addresss.
STORE-VALUE; WITH: number; AT: slot-address; Числовое значение number-сохраняется в элементе с меткой slot-address. После завершения выполнения этой команды данный элемент не будет зависеть от других элементов.
BLANK; SLOT: slot-address; Элемент с меткой slot-address становится пустым. QUIT; Выполнение программы TriviCalc прекращается и управление передается операционной системе.
Приводимые ниже программы влияют на позицию текущей ячейки рабочей области.
]Р Текущая ячейка изменяется и становится ячейкой, расположенной вертикально над текущей, если таковая имеется. Если строка текущей ячейки имеет номер 1, то команда не оказывает никакого эффекта.
IN Текущая ячейка изменяется и становится ячейкой, расположенной вертикально под текущей, если таковая имеется. Если строка текущей ячейки имеет номер 21, то команда не оказывает никакого эффекта.
1В Текущая ячейка изменяется и становится ячейкой, расположенной слева от текущей, если таковая имеется. Если столбец текущей ячейки есть А, то команда не оказывает никакого эффекта,
•fF Текущая ячейка изменяется и становится ячейкой, расположенной справа от текущей, если таковая имеется. Если столбец текущей ячейки есть Н, то команда не оказывает никакого эффекта.
Бинарные команды оператора используют три аргумента, каждый из которых есть метка элемента из области сохранения. Команды связывают элементы области, заданные метками в первых двух аргументах, с результатом, расположенным в элементе области сохранения, заданным меткой в третьем аргументе, Если siot3 помечает элемент, который уже является результатом операции,
Набор заданий по программированию
то старое отношение отбрасывается и устанавливается новое Все эти команды имеют форму
ор: ЗНАЧЕНИЕ1: sloth ЗНАЧЕНИЕ2: slot2; GIVING: slot3; Op может иметь следующие значения:
ADD или + Элемент области сохранения с меткой slot3 относится к двум другим элементам как (sloti + slot2).
SUBSTRACT или — Элемент области сохранения сметкой slot3 относится
к двум другим элементам как (sloti — slot2).
MULTIPLY или ^ Элемент области сохранения с меткой slot3 относится
к двум другим элементам как (sloti + slot2).
DIVIDE или / Элемент области сохранения с меткой slot3 относится к двум
другим элементам как (sloti + slot2).
Редактор рабочей области
Редактор рабочей области представляет собой простейший редактор со специальными функциями, имеющими целью упростить ввод команд в процессор команд TriviCalc. Редактор управляет курсором в рабочей области. Каждое нажатие на клавишу рассматривается как команда редактору. Все команды являются атомарными; они либо обрабатываются целиком немедленно, либо прерываются по ошибке, не выполняя никаких функций, возможно, за исключением отображения сообщения об ошибке. Нажатия на некоторые клавиши отмечают ввод символьных значений (клавиши символов, чисел и знаков пунктуации). Команда, выполняемая при нажатии таких клавиш, просто помещает значение, установленное для данной клавиши, перед курсором. Такие команды называются текстовыми командами ввода.
Остальные клавиши не вводят текстовые значения. Это либо специальные клавиши (например, клавиши возврата каретки или клавиша забоя), либо клавиши, нажимаемые при предварительно нажатой клавише CONTROL. Эти нетекстовые клавиши интерпретируются редактором как команды, обрабатывающие текст в рабочей области. Ниже приводится краткое описание нетекстовых команд _
L Передвигает курсор на одну позицию влево. R Передвигает курсор на одну позицию вправо.
D Удаляет символ, на котором находится курсор, если таковой имеется» DELETE Удаляет символ слева от курсора, если таковой имеется. •JA Operator Adjust: Если рабочая область имеет вид
sloti ор slot2
где ор есть один из символов *./, "—" или "+", а sloti и slot2 есть символьные строки, то содержимое рабочей области заменяется на
ор ЗНАЧЕНИЕ1: sloti; ЗНАЧЕНИЕ2: slot2; GIVING: %;
В противном случае — если содержимое рабочей области представляет собой числовое значение, то рабочая область интерпретируется как
а его содержимое заменяется на СОХРАНИТЬ-ЗНАЧЕНИЕ; WITH: число. AT: %', В противном случае рабочая область интерпретируется как "строка"
и ее содержимое заменяется на СОХРАНИТЬ-КОММЕНТАРИИ; WITH: строка-, AT: %•,
412 Приложение В
Наконец, имитируется эффект команды fK с курсором в начале рабочей области, за которой следует команда ^Е. В результате чего символ % заменяется адресом текущей ячейки.
1К Отыскивр.ет символ %, начиная от позиции курсора, возвращаясь назад к началу рабочей области, если был достигнут ее конец. Если символ % отыскивается, то он удаляется, а курсор остается на прежней позиции. Если символ не обнаруживается, то ничего не происходит.
•[•Е Адрес текущей ячейки в области сохранения помещается на местоположение
курсора.
RETURN Передает содержимое рабочей области процессору команд для немедленного выполнения. Если интерпретатор команд не сигнализирует об ошибке, то содержимое рабочей области удаляется.
LINE-FEED Аналогично команде RETURN, однако содержимое рабочей области не изменяется в обоих случаях.
IV Вставляет текстовое представление содержимого текущей ячейки
в рабочую область по месту курсора.
Удаляет все содержимое рабочей области.
Рабочая область должна иметь форму [цифра ) [строка), где [цифра] есть цифра от 1 до 9. Эта команда приводит к сохранению [строка] по адресу [цифра]. При начале работы с TriviCalc первые четыре внутренние ячейки есть строки 1: SAVE; FILE: %; 2: LOAD; FILE: %; 3: QUIT; 4: BLANK; SLOT: %; Внутренние ячейки 5—9 изначально содержат пустую отроку.
Рабочая область должна иметь форму [цифра] [строка], где [цифра) есть цифра от 1 до 9, а [отрока] может быть пустой. Эта команда приводит к замене содержимого рабочей области строкой, хранимой во внутренней ячейке [цифра]. В этом случае имитируется эффект выполнения команд fK fE, т. е. символ % -заменяется адресом текущей ячейки.
Нетекстовая команда каждый раз вызывает изменение рабочей области, содержимое которой первый раз сохраняется в ячейке 0. Ввод команды f Х в том случае, когда рабочая область имеет форму 0 [строка ], приводит к перемене местами содержимого рабочей области и содержимого внутренней ячейки 0.
Т" 1W
IX
Расширения
Если ваш проект закончен, то, помимо обычных требований, вы можете коротко рассмотреть (будет достаточно одного или двух параграфов) несколько расширений программы TriviCalc. Не реализуйте ни одно из этих расширений (независимо от того, имеется ли у вас время или нет)! Для каждого возможного расширения укажите: 1) более подробное описание каждого изменения; 2) является ли предложенная идея хорошей (и почему вы так думаете); 3) как она может повлиять на вашу программу (какие модули должны быть изменены и т. д.); 4) сколько человеко-часов необходимо для реализации расширения. Ниже приводятся возможные расширения:
1. Расширение рабочей области до массива размерности 100Х100. (Рассмотрите адресацию к ячейкам, управление экраном и т. д.)
2. Возможность существования циклических зависимостей. (Необходимо, чтобы подобное расширение «было осмысленным», поскольку в текущей версии TriviCalc оно не имеет никакого смысла.)
Набор заданий по программированию
413
3. Добавление двух отношений с несколькими аргументами, связывающими совокупность элементов из области сохранения, расположенной в одной строке или в одном столбце, с каким-либо другим элементом области, не принадлежащим данной совокупности. Одно отношение предполагает, что результирующий элемент является суммой элементов данной совокупности, а другое предполагает, что результирующий элемент является средним значением для данной совокупности. Эти отношения имеют форму
SUM; FROM: sloti; THROUGH: slot2; GIVING: slot3; AVERAGE; FROM: sloti; THROUGH: slot2; GIVING: slot3;
Sloti и slot2 должны находиться либо в одном столбце, либо в одной строке, а все элементы области «между» двумя выбранными элементами должны иметь содержимое типа ЗНАЧЕНИЕ. Сумма или среднее значение вычисляются для всех ячеек в строке или столбце, которые находятся между sloti и slot2 включительно.
Полезные указания
В данном разделе дается ряд советов, которые помогут вам в создании реализации.
1. Обработка команд. В обработке команд имеется два базовых этапа. Первым из них является синтаксический анализ — процесс разбиения команды на отдельные. аргументы. Вторым является интерпретация команды — ее обработка. Опыт показывает, что четкое разделение этих этапов оказывается весьма полезным.
2. Отображение значений. Здесь может оказаться полезной процедура g_form, описанная в приложении А.
3. Обработка экрана. Для обновления отображения на экране интерактивной системы существует два распространенных приема. При использовании первого из них каждая команда определяет, какие изменения должны быть сделаны на экране, а затем выполняет эти изменения. В другом случае команда регистрирует каждое изменения при помощи модуля, выполняющего функции менеджера экрана, который поддерживает копию того, что должно быть отображено на экране после выполнения команды. При завершении команды об этом уведомляют менеджера экрана, который затем обновляет экран, отражая все сделанные изменения. Опыт показывает, что второй способ более предпочтителен.
4. Область обновления. Имеется проблема, заключающаяся в необходимости обновления всех значений, зависимых от изменяемого элемента. Дается ряд советов к решению этой проблемы:
1. Необходимо иметь возможность получения значений хранимого элемента тремя способами: по адресу ячейки, по предшественнику к этому элементу, и по элементу, следующему за данным.
2. При модификации командой области сохранения она должна делать это атомарно. Это означает, что если в процессе модификации, необходимой при обработке команды, возникает ошибка, то команда не должна порождать сетевой эффект. Реализовать такое поведение можно несколькими способами. Два из них есть (1) возврат назад (уничтожение всех изменений и перезапись области сохранения) и (2) теневое копирование (выполнение изменений над копией области сохранения и замена области полученной копией только в том случае, если операция завершилась успешно). Последний метод более предпочтителен.
3. Имеется «правильный» порядок вычисления новых значений элементов области сохранения, который зависит от самих этих элементов. Важно вычислять все элементы области, от которых (явно или неявно) зависит данный элемент S, до момента вычисления S. Это не единственный способ, позволяющий получить правильный окончательный результат (в том случае, если ошибок не было), поскольку было бы достаточно повторно вычислить все элементы области сохранения .в порядке глубины их вложения, начиная с измененного элемента. В этом случае значения установятся в правильные. Проблема заключается в том, что
414 Приложение В
такой прием может привести к появлению непреднамеренных переполнении, делении на нуль и т. д., что не произойдет, если элементы будут вычисляться в правильном порядке.
4. Одним из способов определения правильного порядка вычисления элементов из области сохранения является присвоение каждому элементу приоритета: приоритет (S) = 1 + максимум (приоритеты элементов, породивших S) где приоритет, равный 1, используется в том случае, если S не имеет прародителей. Если элементам области сохранения присваиваются значения приоритета именно таким образом, то оптимальный порядок вычислений упорядочен по возрастанию номера приоритета. Отметим, что при изменении некоторого элемента из области сохранения можно определить те элементы, которые также, возможно, будут изменены. Это означает, что нет необходимости при обработке каждой команды вычислять все элементы.
5. Вспомогательное программное обеспечение. Мы приведем реализацию абстракции, которая поможет вам при создании интерфейса к терминалам. Тип данных screen обеспечивает примитивный интерфейс к экрану терминала, подобного VT-100; ее спецификация приведена на рис. В.5. Литералы языка CLU для нетекстовых символов приведены в табл. B.I. При чтении с терминала исполь зуйте операцию stream$getc- image (это позволяет избежать «эхо»-эффекта).
Табл. В.]. Литералы нетекстовых символов языка CLU
Символ |
Литерал CLU |
Символ |
Литерал CLU | ||
|
А |
i\OOU |
|
Р |
'\020' |
|
В |
'\002' |
|
R |
•\022' |
|
D |
'\004' |
|
U |
'\025' |
|
Е |
'\005' |
|
V |
^026' |
|
F |
»\006' |
|
W |
'\027- |
|
К |
'\013' |
|
X |
'\030' |
|
L |
i\014' |
DELF |
ETE |
'\077' |
|
N |
^016' |
RETURN |
- ^i" | |
|
|
LINE-FEED |
•Vn- |
screen = data type is create, clear, get_height, get-width, putc, puts, flush, beep, cursor-forward, cursor-backward, cursor-up, cursor-down, index, reverse-index, set-cursor-pos, special-modes, erase-to-eol, erase- to- bottom
Описание
Экран представляет собой абстрактное представление первичного устройства вывода. Он может Сыть использован только в том случае, если имеете^ поддержка для первичного выводного устройства. Кроме этого, программа может создавать только один объект типа «экран».
Экран является матрицей символов, организованных по строкам и столбцам вместе с курсором. В качестве символов может выступать любой символ в коде ASCII, чей код находится в интервале 32—126 включительно. Отметим, что в данный набор не входит символ табуляции и символ перехода к новой строке. Чистый экран—это экран, матрица которого целиком заполнена символами пробела. Строки пронумерованы сверху вниз, начиная с 1, а столбцы пронумерованы слева направо, также начиная с 1. Высота экрана равна числу строк, а ширина экрана равна числу столбцов.
Набор заданий по программированию
В любой момент времени курсор указывает на один из символов матрицы. Его позиция также определяется строкой и столбцом. Положение курсора влияет на поведение большинства модифицирующих экран операций, которые производят модификации по отношению к текущей позиции курсора. Имеются операции для перемещения курсора по экрану. Есть также операции по перемещению изображения на экране вверх и вниз. При перемещении всего изображения на экране вверх каждая строка экрана сдвигается вверх на одну строку. Верхняя строка «исчезает» с экрана, а в нижнюю строку записывается пустая строка. Перемещение вниз обратно перемещению вверх.
Символы, выводимые на экран операциями puts и putc, буферизуются системой до тех пор, пока не произойдет одно из следующих событий: (1) символ считывается из первичного устройства ввода или (2) вызывается неявно очищающая буфер операция над кластером экрана. Явный вызов операции flush необходим только в том случае, если выводимое на экран изображение должно появляться на нем в то время, когда программа пишет символы на экран, но не вводит их с первичного устройства ввода. Примером может служить отображение статуса программы в процессе выполнения ею некоторых вычислений.
Операции
create = proc ( ) returns (scrn: cvt) signals (error (string))
modifies primary-input
effects Создает и возвращает объект «экран» для первичного устройства вывода. Очищает экран и перемещает курсор в левый верхний угол экрана. Сигнализирует error («данный тип терминала не поддерживается»), если для данного первичного устройства вывода отсутствует программная поддержка.
clear == proc (scrn: cvt) modifies scrn
effects Очищает экран и перемещает курсор в верхний левый угол экрана (позиция курсора [1, 1]).
get-height = proc (scrn: cvt) retuips (heigth: int)
effects heigth = число строк, которые могут быть отображены на экране одновременно.
get-width == proc (scrn: cvt) returns (width: int)
effects width = числу столбцов, которые могут быть отображены на экране одновременно.
putc == proc (scrn: cvt, ch: char)
requires ch должна быть либо печатным символом (коды ASCII 32—126 включительно), либо символом возврата каретки, либо символом перехода к новой строке modifies scrn effects Зависит от типа ch следующим образом:
ch должна быть печатным символом с кодами ASCII в диапазоне 32—126 включительно (отметим, что в этот набор не входит символ пробела). Символ отображается по месту текущей позиции курсора. Если курсор не находится в последнем столбце, то он перемещается на одву пози-- пик) вправо. В противном случае он остается в последнем столбце.
ch есть символ возврата каретки (\г). В этом случае курсор перемещается в первый столбец той строки, на которой он находится в текущий момент (если начальная позиция курсора есть [х, у], то конечная его позиция будет [х, 1 ]).
416 Примясение В
ch есть символ перехода к новой строке. Если курсор расположен не на последней строке экрана, то он перемещается на следующую строку, оставаясь при этом в том же самом столбце (если позиция курсора есть 1_х, у1, то его окончательная позиция будет [х+ 1, у]). Если курсор находится на последней строке экрана, то все изображение на экране перемещается вверх на одну строку, а позиция курсора не изменяется. Эта операция также уничтожает любые выводимые данные, которые могли быть буферизованы. Отметим, что этот случай аналогичен исполь-зованию операции reverse, index.
puts = proc (scrn: cvt, str: string)
requires Каждый символ в строке str должен быть либо печатным символом (ASCII код 32—126 включительно), либо символом возврата каретки или перехода к новой строке. modifies scrn
effects Для каждого символа р в строке str выполняется операция putc {scrn, с). Отметим, что это более эффективно, чем использование операции putc для каждого символа в строке.
flush = proc (scrn: cvt) effects Очищает буферизованные для вывода на экран данные.
beep == proc (scrn: cvt) modifies scrn
effects Очищает буферизованные для вывода на экран данные и выдает тональный сигнал на терминал.
cursor, forward == proc (scrn: cvt, n: int)
modifies scrn
effects Передвигает курсор на n столбцов вправо. Курсор остается на той же строке. После достижения курсором крайнего правого столбца в строке он перестает перемещаться вне зависимости от значения числа n.
cursor, backwards proc (scrn: cvt, n: $nt)
modifies scrn
effects Передвиглт курсор на n столбцов влево. Курсор остается на той же строке. После достижения курсором крайнего левого столбца в строке он перестает перемещаться вне зависимости от значения числа n.
cursor, up = proc (scrn: cvt, n: int)
modifies scrn
effects Передвигает курсор на n строк вверх. Курсор остается в том же столбце. После достижения курсором верхней строки он перестает перемещаться вне зависимости от значения числа n.
cursor, down = proc (scrn: cvt, n: Int)
modifies scrn
effects Передвигает курсор на п строк вниз. Курсор остается в том же столбце. После достижения курсором нижней строки он перестает перемещаться, вне зависимости от значения числа n.
Index == proc (scrn: cvt) modifies scrn
effects Перемещает курсор по экрану на следующую строку (вниз). Курсор остается в том же столбце. Если при вызове данной процедуры курсор уже находится на последней отроке, то изображение на экране перемещается вверх на одну строку, а позиция курсора не изменяется. Буферизованные выходные данные стираются.
Набор заданий по программированию
reverse, index = proc (scrn: cvt)
modifies scrn
effects Перемещает курсор на предыдущую строку (вверх) по экрану. Курсор остается в том же столбце. Если при вызове процедуры курсор уже находится в верхней строке, то изображение на экране перемещается на одну строку вниз, а позиция курсора при этом не изменяется. Буферизованные выходные данные стираются.
set- cursor, pos== proc (scrn: cvt, x, y: int) signals (bounds) ,
modifies scrn
effects Перемещает курсор на строку x и столбец у экрана, если такая позиция существует. В противном случае сигнализирует bounds.
special, mode == proc (scrn: cvt, reverse.video, underline: bool)
modifies scrn
effects Устанавливает для терминала режим инверсного изображения, в зависимости от значений двух аргументов. Символы, отображавшиеся на экране перед установкой режима, не изменяются. После установки режима все дальнейшие отображаемые на экране символы отображаются в этом новом режиме. Если терминал не поддерживает такой режим, то операция не оказывает никакого эффекта.
erase, to. ео1 == proc (scrn: cvt)
modifies scrn
effects Стирает изображение в текущей строке, начиная с курсора и кончая позицией, заданной столбцом screen$width включительно. Стирает любые буферизованные выводимые данные.
erase, to. bottom = proc (scrn: cvt)
modifies scrn
effects Стирает экран, начиная с позиции, заданной курсором, и до конца экрана. Стирает любые буферизованные выводимые данные.
end screen Рис. В.5. Спецификация абстракции screen.