Эта работа была поддержана Управлением перспективных исследовательских проектов Министерства обороны (F44610-70-C-0107) и контролируется Управлением научных исследований ВВС. 

Аннотация

Исследуется роль документации в проектировании и реализации сложных систем; в результате предлагаются рекомендации, резко контрастирующие с современной практикой. Для изучения понятия структуры системы анализируется смысл фразы «связи между модулями». Показано, что несколько целей проектирования системы (каждая из которых предполагает частичное упорядочение решений по времени) могут быть несовместимы. Обсуждаются некоторые свойства программистов. Обсуждается системная документация, делающая всю информацию доступной любому, кто работает над проектом. Тезис о том, что такое «вещание» информации вредно и что полезно, если большую часть информации о системе можно скрыть от большинства программистов, подкрепляется использованием вышеупомянутых соображений, а также примерами.

1. Введение

Статьи по методологии проектирования исходят из того, что (1) методы, используемые при проектировании систем, сильно влияют на качество конечного продукта; и (2) что, выбрав подходящую методологию, мы можем избежать многих проблем, с которыми прежде сталкивались при конструировании больших систем.

В разделе «Методология проектирования» можно выделить ряд отдельных тем:

  • Порядок принятия проектных решений [1, 2, 3, 6].

  • Характеристики конечного продукта (например, что представляет собой «хорошая структура» системы) [4, 5, 6, 7].

  • Методы обнаружения ошибок в проектных решениях вскоре после того, как они были допущены [1, 2, 3, 5, 8, 9].

  • Техники спецификации [12, 13].

  • Инструменты системных проектировщиков [1, 2, 3, 10, 11].

В этой статье основное внимание уделяется другой теме — «распределению информации». Проектирование и разработка представляют собой ряд решений. Каждое решение дает информацию о системе, которую можно использовать при принятии последующих решений. В итоге мы хотим обсудить распределение этой информации среди тех, кто работает над системой, и рассмотреть, как она должна быть организована в документации. Чтобы подготовиться к этому обсуждению, мы сначала рассмотрим (1) понятие структуры системы, (2) ограничения на порядок принятия решений и (3) некоторые наблюдаемые характеристики хороших программистов.

2. Определение структуры

Слово «структура» используется для обозначения частичного описания системы. Описание структуры показывает систему, разделенную на набор модулей, приводит некоторые характеристики каждого модуля и указывает некоторые связи между модулями. Любая данная система допускает множество таких описаний. Поскольку описания структуры не уникальны, наше употребление слова «модуль» не позволяет дать точное определение, аналогичное определению «подпрограммы» в программном обеспечении или «карты» в аппаратном. Определения этих слов очерчивают класс объектов, а определение слова «модуль» — нет. Тем не менее слово «модуль» полезно в том же смысле, в каком слово «единица» полезно в военной или экономической сфере. Мы будем и далее использовать слово «модуль» без точного определения. Оно относится к частям системы, указанным в описании этой системы. Его точное определение зависит не только от системы, но и от конкретного рассматриваемого описания.

Термин «связь» (connection) обычно принимается более охотно. Многие полагают, что «связи» — это точки передачи управления, передаваемые параметры и общие данные для программного обеспечения, провода или другие физические соединения для аппаратного обеспечения. Такое определение «связи» — крайне опасное упрощение, порождающее обманчивые описания структуры. Связи между модулями — это предположения, которые модули делают друг о друге. В большинстве систем мы обнаруживаем, что эти связи гораздо шире, чем последовательности вызова и форматы блоков управления, которые обычно приводятся в описаниях структуры системы.

Смысл приведенного выше замечания можно показать, рассмотрев две ситуации, в которых структура системы крайне важна: (1) внесение изменений в систему и (2) доказательство правильности системы. (Я не считаю нужным обосновывать необходимость доказательства правильности программ или отстаивать необходимость внесения изменений. Я хочу использовать эти гипотетические ситуации, чтобы показать смысл слова «связь».)

Доказательства правильности программ могут стать настолько сложными, что под вопросом оказывается их собственная правильность (например, [14], [15]). Для больших систем при построении доказательств необходимо использовать структуру программ. Мы должны рассматривать программы, составляющие каждый модуль, по отдельности. Для каждого модуля мы определим (1) свойства системы, которые он призван гарантировать, и (2) свойства, которых он ожидает от других модулей. Доказательство правильности для каждого модуля будет рассматривать (1) как множество теорем, подлежащих доказательству, и (2) как множество аксиом, которые можно использовать при доказательстве того, что программы действительно гарантируют эти свойства системы. Доказательства теорем, полученных для каждого модуля, будут использоваться при доказательстве правильности всей системы. Утверждения (1) и (2) составляют связи между различными модулями системы. Такой подход облегчит задачу доказательства правильности системы, если объем информации в множествах утверждений (1) и (2) значительно меньше, чем информация в полном описании программ, реализующих связанный модуль.

Теперь рассмотрим внесение изменений в завершенную систему. Мы задаемся вопросом: «Какие изменения можно внести в один модуль, не требуя изменений в других модулях?» Мы можем вносить только такие изменения, которые не нарушают предположений, которые другие модули делают относительно изменяемого модуля. Иными словами, один модуль может быть изменен, только если «связи» все еще «стыкуются». Отсюда следует еще один сильный аргумент в пользу того, чтобы связи содержали как можно меньше информации

3. Факторы, влияющие на порядок принятия решений

Прогресс в проектировании отмечается решениями, которые исключают некоторые возможности для структуры системы. Тот факт, что эти возможности были исключены, может быть частью обоснования последующих решений. Если эта информация используется, порядок принятия решений (во времени) влияет на структуру итогового продукта. Интересные примеры можно найти в [4]. Мы можем выделить три обстоятельства, каждое из которых предполагает частичное упорядочение решений.

3.1. Достижение «хороших» внешних характеристик

Все системы обладают характеристиками, которые не нравятся пользователям. Как правило, специально их никто не продумывал; они были незамеченными следствиями решений о других аспектах структуры системы. Чтобы последовательно избегать подобных ошибок, мы можем сначала принимать решения о внешних характеристиках, а затем использовать полученную информацию для принятия последующих решений. Внутренние решения будут либо выводиться из полных спецификаций внешних факторов, либо сверяться с ними. Это основа подхода «сверху вниз», или «снаружи внутрь», рассматриваемого в [1, 2, 3, 4].

3.2. Сокращение временного интервала между началом и завершением проекта

Давление конкуренции может потребовать задействования больших групп для создания системы в крайне сжатые сроки. Дополнительные сотрудники значительно ускоряют проект только после того, как он будет разделен на подпроекты таким образом, чтобы отдельные группы могли работать почти не взаимодействуя (то есть тратя значительно меньше времени на межгрупповые решения, чем на внутригрупповые). Это обстоятельство влияет на порядок принятия решений, поскольку побуждает как можно раньше разделять систему на модули, которые затем проектируются совершенно независимо. Желание рано произвести разделение и «двигаться дальше» способствует тому, что система делится по уже привычным границам и в соответствии со сложившимися классификациями персонала.

Давление сроков побуждает группы проводить разделение до того, как определены внешние характеристики. В результате мы наблюдаем неблагоприятное влияние на удобство использования продукта. Спешка также повышает вероятность плохой внутренней структуры.

3.3. Получение легко изменяемой системы

Системы изменяются после создания либо потому, что их исходные характеристики оказались недостаточными, либо потому, что им нашлось другое применение. Мы уже отмечали, что трудности при изменении систем связаны с предположениями, которые каждый модуль делает о своем окружении. Поскольку каждое решение обычно принимается исходя из предположения, что предыдущие решения останутся в силе, труднее всего обычно изменить те решения, которые были приняты раньше всего. Последний добавленный фрагмент кода легко изменить, однако фрагмент, добавленный несколькими месяцами ранее, мог врасти в программу, так что его будет трудно извлечь. Эти обстоятельства предполагают, что первыми следует принимать те решения, которые с наименьшей вероятностью будут изменены; то есть решения, основанные на «универсальных» истинах или на соображениях, которые мало учитывают конкретное окружение. Оставшиеся факты должны быть в конечном счете использованы, но возможность изменения предполагает использование наиболее общей информации в первую очередь.

Поскольку такие внешние характеристики, как язык управления заданиями и файловые команды, меняются очень часто, подход «снаружи внутрь» может затруднить изменение системы. Более того, решения, принятые на раннем этапе исходя из этого подхода, обычно не позволяют быстро разбить проект на независимые задания. Как правило, решения, в которых не используется вся доступная информация о системе (то есть общие решения), требуют больше времени.

Таким образом, каждое из этих трех обстоятельств предполагает частичное упорядочение решений. Эти упорядочения, как правило, несовместимы в том смысле, что невозможно одновременно следовать им всем.

4. Системы документации

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

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

Естественной реакцией будет требование, чтобы вся документация была написана с использованием стандартной организации и стандартной терминологии [16]. Стандарт вводится в масштабе всей компании, чтобы любой сотрудник мог найти нужную информацию, не осваивая понятия и терминологию, присущие отдельной системе или модулю.

Такие подходы вызывают несколько вопросов: 

  1. Действительно ли желательно, чтобы вся информация была одинаково доступна всем в компании (или на проекте)?

  2. Как стандарты документации влияют на итоговую систему? 

  3. Что получается, если нестандартную систему описывают с использованием стандартной организации документа?

Стандарты документации обычно втискивают структуру системы в стандартный шаблон. Стандарт организации документации и терминологии содержит некоторые предположения о структуре описываемой системы. Если эти предположения нарушаются, организация документации плохо соответствует системе, а термины приходится расширять или употреблять не по назначению. Рассмотрим следующий пример. В большинстве операционных систем есть модуль, обрабатывающий все команды управления заданиями с момента их считывания до завершения задания. В результате большинство систем документации могут требовать наличия раздела с описанием такого модуля. Теперь рассмотрим организацию (как у системы T.H.E. [18]), в которой такой модуль отсутствует, поскольку большая часть обработки происходит в модулях, которые также используются для других целей. Если мы будем следовать стандарту документации, то будем дублировать информацию и описывать один модуль в документации другого. 

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

5. О некоторых свойствах хороших программистов

Следующее наблюдение имеет ключевое значение для остальной части статьи:

«Хороший программист использует всю доступную ему информацию!»

Хороший программист постарается эффективно использовать свою машину. Фактически он программирует для «виртуальной машины», определяемой аппаратным обеспечением и его знанием другого программного обеспечения на этой машине. Выучка и натура ведут его к тому, чтобы в полной мере использовать эту расширенную машину.

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

Иногда способы использования менее очевидны. Например, программист может использовать свое знание того, что список просматривается в определенном порядке, чтобы исключить проверку или дополнительную очередь. В области прикладного программирования можно встретить программиста, который вводит ошибочное значение π, зная, что из-за ошибки в подпрограмме вычисления синуса это ошибочное значение заставит его программу сходиться быстрее.

Подобное использование информации обходится так дорого, что сейчас мы наблюдаем странную реакцию. В отрасли стали поощрять плохое программирование. Уничижительные прозвища вроде «костыльщик», «кустарщик» и «жонглер битами» используются для обозначения тех, кто пишет крайне заумные программы, которые впоследствии приносят проблемы. Таких программистов незаметно, но эффективно оттесняют, поручая им работу над небольшими независимыми проектами — такими как прикладные подпрограммы (Сибирь мира программного обеспечения) или диагностические подпрограммы для аппаратного обеспечения (угольные шахты). И в том, и в другом случае у программиста мало возможностей использовать информацию о других модулях.

Оставшиеся (те, кто не жонглирует битами) — как правило, плохие программисты. И хотя некоторые воздерживаются от использования информации, зная, что это приведет к проблемам, большинство воздерживается просто потому, что не видит, как эту информацию можно использовать. Такие люди также упускают возможность использовать те факты, которые следовало бы использовать. В результате получаются плохие программы. А поскольку даже у плохого программиста порой бывает «проблеск гениальности» (например, он замечает, что два байта в блоке управления можно одновременно установить одной инструкцией, потому что они смежные и находятся в одном слове), мы по-прежнему не контролируем структуру.

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

6. Использование распределения информации, контролируемого проектировщиками

Мы можем избежать многих из обсуждаемых здесь проблем, если откажемся от представления, что проектная информация должна быть доступна каждому. Вместо этого мы должны позволить проектировщикам — тем, кто определяет структуру, — контролировать распределение проектной информации по мере ее накопления.

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

Если мы хотим, чтобы структуру определяли проектировщики, они должны иметь возможность контролировать ее, контролируя распределение информации. Мы не должны ожидать, что программист решит не использовать какую-то информацию; напротив, у него не должно быть информации, которую ему не следует использовать. Это решение — часть проектирования, а не программирования.

Если задуматься, становится ясно, что такой подход требует от проектировщиков очень многого. Сейчас мы делаем доступной всю информацию о модуле; это значительно проще, чем (1) решать, какая информация должна быть раскрыта, и (2) находить способ выразить именно ту информацию, которая нужна другим модулям. Предварительный опыт показал, что составление подходящих определений — дело довольно трудное. Приобрести навык составления таких определений жизненно важно, потому что успешно строить системы, ограничивая информацию, доступную программистам, мы сможем только в том случае, если научимся предоставлять им ровно ту информацию, которая им нужна.

7. Примеры

Полагаю, стоит привести несколько конкретных примеров информации, которая сейчас широко распространяется вместе с проектом, а вместо этого должна быть жестко ограничена.

7.1. Форматы блоков управления

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

7.2. Карты памяти

Описание операционной системы часто начинают с (1) описания основных модулей и (2) демонстрации того, как основная память распределяется между этими основными модулями. Вскоре появляется полная карта памяти, показывающая, как распределяется этот ресурс. Достаточно опытные проектировщики показывают границы выделенных областей в виде символических, а не абсолютных адресов, однако порядок распределения памяти указывается. Лишь небольшая часть этой информации исходит из аппаратных решений. Нет никакого оправданного способа использовать информацию этой карты. Страшно представить, что кто-то напишет код, который не будет работать при ее изменении. Подобные карты почти всегда меняются: постоянное становится переменным, и наоборот. Эта информация требуется лишь на этапе ассемблирования. Мы бы ничего не потеряли, если бы она вводилась в ассемблер и не была известна никому другому.

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

7.3. Последовательности вызова

Последовательности вызова — тайное хобби каждого системного программиста. Мы начинаем изучать новое аппаратное обеспечение, придумывая последовательность вызова. В ходе проектирования и реализации последовательность вызова упрощается, обобщается, делается более эффективной и т.д. Каждый раз приходится принимать решение. Либо изменяются модули по всей системе, либо к растущему множеству последовательностей вызова добавляется новая. В последнем случае генерация вызова подпрограммы требует определить, какую последовательность она использует.

Большинство подпрограмм может быть написано, и написано хорошо, без знания последовательности вызова, если программист обеспечен инструментом, позволяющим ему отложить решения о распределении регистров для параметров, адресов возврата и результатов. Такие возможности могут быть предусмотрены в ассемблере, поддерживающем макросы.

7.4. Форматы JCL

Одна из характеристик, которая должна легко изменяться, — это синтаксис так называемого языка управления заданиями (Job Control Language) — средства, с помощью которого пользователь описывает общие характеристики своего задания операционной системе. В основе проектирования JCL лежат предположения о том, как будет использоваться система. Впоследствии эти предположения могут оказаться ложными или налагать излишние ограничения. Существуют системы, в которых информация о формате JCL использовалась настолько широко, что разумные изменения требуют, чтобы пользователь предоставлял дублирующую информацию и/или поддерживал дублирующие таблицы. (См., например, [17].)

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

7.5. Размещение адресов устройств ввода-вывода

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

7.6. Символьные коды

Некоторую информацию об аппаратном обеспечении не следует раскрывать. Я видел один компилятор, в котором связь между кодами символов перфокарт и целыми числами, устанавливаемая аппаратным обеспечением, использовалась настолько широко, что вторая версия компилятора (для новой машины) содержала модуль, преобразующий новый символьный код в старый и обратно.

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

8. Заключение

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

Я считаю внутреннее ограничение доступа к информации внутри групп разработки гораздо более важным, чем ограничение доступа к ней для пользователей или конкурентов. Значительная часть информации в документации системы лишь навредила бы конкуренту, если бы она у него была. (Ведь он мог бы ею воспользоваться!)

Стоит повторить, что решение о том, какую информацию ограничивать, — это проектное решение, а не управленческое. Ответственность на уровне управления заканчивается предоставлением подходящего механизма распределения информации. Использование этого механизма остается функцией проектирования, поскольку оно определяет структуру продукта.

Благодарности

Я благодарен А. Перлису, Г. Уоктлару и Ч.Г. Беллу за их замечания по раннему варианту этой статьи. Я глубоко признателен компании NV Philips-Electrologica (Апелдорн, Нидерланды) за предоставленную мне возможность изучать проблемы разработки систем через непосредственное участие. Хотя с проблемами, обсуждаемыми в этой статье, сталкиваются все представители отрасли, меры, предпринятые в Philips для улучшения этой ситуации, натолкнули меня на важные идеи. Я также благодарен многочисленным сотрудникам Philips и нескольких других учреждений за терпение, которое они проявляли в ходе моих изысканий.

Источники

[1] Parnas D.L., Darringer J.A. SODAS and a Methodology for System Design // Proceedings of the AFIPS 1967 Fall Joint Computer Conference, vol. 31, 1967.

[2] Zurcher F.W., Randell B. Iterative Multi-Level Modeling — A Methodology for Computer System Design // Morrell A.J.H. (ed.) Information Processing 68: Proceedings of IFIP Congress 1968. Vol. 2. Amsterdam: North-Holland Publishing Company, 1969.

[3] Parnas D.L. More on Simulation Languages and Design Methodology for Computer Systems // Proceedings of the AFIPS 1969 Spring Joint Computer Conference, vol. 34, 1969.

[4] Дейкстра Э. Заметки по структурному программированию // Дал У.-И., Дейкстра Э., Хоор К. Структурное программирование. М.: Издательство «Мир», 1975.

[5] Dijkstra E.W. Complexity Controlled by Hierarchical Ordering of Function and Variability // Naur P., Randell B. (eds.) Software Engineering: Report on a Conference Sponsored by the NATO Science Committee, Garmisch, Germany, October 7–11, 1968. Brussels: Scientific Affairs Division, NATO, 1969

[6] Gill S. Thoughts on the Sequence of Writing Software // Naur P., Randell B. (eds.) Software Engineering. Brussels, 1969.

[7] Dijkstra E.W. Structured Programming // Buxton J.N., Randell B. (eds.) Software Engineering Techniques: Report of a Conference Sponsored by the NATO Science Committee, Rome, Italy, October 27–31, 1969. Brussels: Scientific Affairs Division, NATO, 1970.

[8] Dijkstra E.W. A Constructive Approach to the Problem of Program Correctness // BIT, vol. 8, № 3, September 1968.

[9] Naur P. Proof of Algorithms by General Snapshot // BIT, vol. 6, № 4, December 1966.

[10] Wulf et al. BLISS Users Manual (публикация Университета Карнеги-Меллона, Питтсбург, Пенсильвания).

[11] Waite V.M. The Mobile Programming System: STAGE 2 // Communications of the ACM, vol. 13, № 7, July 1970.

[12] Parnas D.L. On the Use of Transition Diagrams in the Design of a User Interface for an Interactive Computer System // Fuller H.W. (ed.) Proceedings of the 1969 24th National Conference (ACM '69). Boston: ACM, 1969.

[13] Hartman P.H., Owens D.H. How to Write Software Specifications // Proceedings of the AFIPS 1967 Fall Joint Computer Conference, vol. 31, 1967.

[14] Balzer R.M. Studies Concerning Minimal Time Solutions to the Firing Squad Synchronization Problem (докторская диссертация в Институте технологии Карнеги, 1966).

[15] London R. Certification of Treesort 3 // Communications of the ACM, vol. 13, № 6, June 1970.

[16] Selig F. Documentation Standards // Naur P., Randell B. (eds.) Software Engineering. Brussels, 1969.

[17] Braden A. et al. An Implementation of MVT (публикация Калифорнийского университета, Лос-Анджелес).

[18] Dijkstra E.W. Structure of the T.H.E. Multiprogramming System // Communications of the ACM, vol. 11, № 5, May 1967.

Комментарии (0)