Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
36
Добавлен:
23.03.2015
Размер:
933.38 Кб
Скачать

15.2. Выбор подхода

В процессе адаптирования языка программирования к методо­логии (и обратно) в качестве руководства могут быть использо­ваны описанные в последнем разделе и гл. 7 подходы для языка Паскаль. Очевидно, что выбранный подход будет сильно зависеть от языка. Желательно везде, где это возможно, эффективно ис­пользовать особенности выбранного языка.

Например, в языке Ада имеется хорошая поддержка для абстракций данных, исключений и полиморфных абстракций. Для этого языка мы продвигались бы в следующем направлении:

1. Использование функций и процедур и определение их интерфейсов в терминах механизмов вызова языка Ада (in, out и inout).

2. Использование исключительных ситуаций языка Ада с со­глашением, что пользователи имеют доступ ко всем приведенным в интерфейсе исключительным ситуациям плюс failure и явная сигнализация о возникновении ситуации failure во всех местах, где ее возникновение регистрировалось бы в языке CLU неявно (или явно). Такое соглашение необходимо по той причине, что язык Ада распространяет сигнал о необработанных исключитель­ных ситуациях автоматически. В отсутствие такого соглашения нет способа гарантировать, чтобы процедура сигнализировала только о ситуациях, перечисленных в ее интерфейсе.

3. Реализация абстракций данных с пакетами и приватными или ограниченными приватными типами.

4. Замена итераторов генераторами. Генератор для типа реали­зуется в том же пакете, который реализует тип.

5. Реализация родовых параметров.

^6. Ассемблирование программ с помощью программного окру­жения языка Ада.

В языке Ада, как и в языке Паскаль, мы должны выбрать для имеющихся абстракций способ организации пространства для хранения их объектов — стек или неупорядоченный массив. Абстракция должна использовать неупорядоченный массив в том случае, если размеры объектов динамически изменяются. В про­тивном случае можно использовать стек.

Типы, использующие неупорядоченный массив, существенно отличаются от типов, использующих стек. Тип, использующий стек, имеет операции создания, а его операции используют пере­дачу •аргументов по значению. Он может также иметь операцию разрушения, если используемый язык не имеет механизмов сборки мусора (случай с языком Паскаль). Эта операция не требуется в языках, обеспечивающих такую поддержку (в случае использо­вания языка Ада). Тип, использующий стек, не имеет этих опера­ций, однако имеет операции инициализации, которые используют

Использование других языков

передачу аргументов по ссылке (или, в случае языка Ада, через значение/результат). (Операции инициализации не требуются, если язык допускает наличие у типа параметров, а также допу­скает использование этих параметров для инициализации нового распределенного пространства. Язык Ада имеет с этой точки зре­ния ограниченные возможности.) Необходимо отметить различие для этих случаев операции присваивания. Для типа, использу­ющего стек, операция присваивания приводит к созданию копии, а для типа, использующего неупорядоченный массив, она вызы­вает разделение объектов. Генераторы стекоориентированных типов также отличаются от генераторов типов, использующих неупорядоченные массивы.

Решение о том, какой тип использует неупорядоченный массив и какой стек, принимается при их создании и отражается в спе­цификации, которая должна явно сообщать, какой из подходов предполагается. Реализация типа должна обеспечивать распре­деление пространства из стека или неупорядоченного массива в строгом соответствии со спецификацией. Использование метода, отличного от указанного в спецификации, является ошибкой.

Различие между этими двумя видами типов имеется также и для параметризованных типов. Параметризованный набор может содержать объекты, передаваемые в качестве аргументов, если э1и объекты находятся в неупорядоченном массиве. Однако в большин­стве языков набор может состоягь из копий объектов, если они находятся в стеке. Это объясняется тем, что большинство языков не поддерживают явных указателей на позиции в стеке (исключе­нием является Алгол 68). Если набор содержит фактические объ­екты, то при доступе к ним из этого набора изменения в объектах будут видны. Если набор содержит копии, то изменения видны не будут.

Поскольку данное различие весьма существенно, невозможно определить тип, который мог бы использовать в качестве пара­метра либо стекоориентированный тип, либо тип на базе неупоря­доченного массива. Вместо этого параметризованный тип должен явно указывать в предложении requires, какой вид типа исполь­зован в качестве параметра.

Пример спецификации параметризованного типа приведен на рис. 15.5. Соглашения об именах соответствуют введенным в гл. 7. Подстановка делается при помощи редактора текста. В данном случае параметризованный тип представляет собой неограничен­ную очередь, следовательно, ее объекты находятся в неупорядочен­ном массиве. Однако предполагается, что тип параметра является стекоориентированным, что констатировалось в предложении re­quires. Отметим, что, поскольку тип параметра является стеко­ориентированным, аргументы этого типа передаются операциям над очередью по ссылке.

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 и должен описывать предварительный проект Tri­viCalc. Каждая группа должна получить и рассмотреть этот документ. Проекты должны быть обсуждены в этот же день. При следующей классной встрече ваш ассистент должен вернуть ваш проект с замечаниями и предложениями по его улучшению. Затем вам дается девять дней на подготовку этого улучшенного про­екта и нового документа. Исправленные проекты обсуждаются в классе Оконча­тельная версия, включая тексты модулей, должна быть готова через девять дней после сдачи исправленного проекта. Оценка снижается, если окоича^льный проект значительно отличается от исходного исправленного.

После того как проект будет готов, каждая группа должна предоставить свою программу. Отчет о выполненном задании ограничен 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. Возможность существования циклических зависимостей. (Необходимо, чтобы подобное расширение «было осмысленным», поскольку в текущей версии Trivi­Calc оно не имеет никакого смысла.)

Набор заданий по программированию

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.

Соседние файлы в папке Б. Лисков