Спецкурс "Культура программирования"

Лекция первая Почему спецкурс называется именно так

(редакция от 08.05.86)

Это сугубо практический спецкурс. Все, что я буду говорить здесь - выстрадано моей 10-летней программистской практикой. Если вы не прочувствуете все это сейчас, потом набьете те же шишки, что и я набил, да и все программисты набивают. Отнеситесь ко всему, что я рассказываю, как к реальности, которая ждет вас через два года

Договорились?

Чтобы вы окончательно поняли, для чего я читаю вам этот спецкурс, расскажу вам одну историю.

Группе из четырех человек поручили создать некую информационную систему. Небольшую. Дали срок 3 месяца, а был март, и, следовательно, закончить систему надо было к июлю.

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

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

Руководитель раскидал задачи по исполнителям, пару раз они все встретились, чтобы обсудить, какой должна быть их система, а потом (они были люди горячие и работы не боялись), недолго думая, взялись за программирование.

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

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

Третий программист (что-то у нас одни мужчины, пусть третьего программиста зовут Наташей) обнаружил (т.е. обнаружила), что ее программа (обработка таблицы 1) распадается на две части : формирование и печать. Сообразив, что , если таблица не сформирована, то печатать нечего, Наташа взялась за модуль формирования, а модуль печати оставила на потом.

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

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

Четвертый программист (пусть это будет Света), тоже начала с программы формирования, но она сразу увидела, что ее программа сложнее и карты полтора на два метра ей не хватит. Она решила пойти другим путем : написать и отладить сначала небольшие кусочки, затем объединить их в кусочки побольше, затем объединить более крупные куски, и так до тех пор, пока не получится законченная программа формирования.

Так они рьяно работали, брали в день по 3-4 часа машинного времени и отлаживались в самозабвении.

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

Итак, Леша подавал записи одного вида, Наташа считала, что там записи другого вида, а Света - вообще третьего и на те два непохожего. Но они об этом не подозревали, так как отлаживались поодиночке, на собственных данных.

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

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

Все утверждали, что их программы готовы на 90%, осталось найти одну-две ошибки, и всё. Поэтому руководство выделило им ночную смену целиком, чтобы, сделав последний рывок, они к августу достигли цели. Подошла пора отпусков, но в отпуск никто не шел: до цели было слишком близко. Думали так: вот отлажусь и пойду в отпуск. Все работали ночью, в субботу, в воскресенье, работали на износ. Шли месяцы, а программы были по-прежнему отлажены на 90%, т.к. обнаруживались все новые и новые ошибки.

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

Машина (и остальные программисты) должны были ждать, пока Наташа сбегает перебьет карту . В том ВЦ, чтобы добежать от машзала до перфозала, нужно было пробежать по подвалу, подняться на первый этаж, попетлять по нему, подняться на второй и бежать до конца коридора, а затем опять спуститься на первый этаж. Набить карту, и - в машзал - все в обратном порядке. Прибежав с перебитой 835 картой, Наташа видела такую картину : ее товарищи сочли ее без вести пропавшей и задание ее сняли. Наташа ставила всю колоду заново и на этот раз рвалась уже 457 карта.

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

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

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

Света отладила, наконец, свои кусочки. начав собирать их в единое целое, она узрела страшную картину. Во-первых, многие кусочки мешали друг другу, используя одни и те же регистры, переменные и т.д. Во-вторых, они часто не стыковались : один кусочек считал, что нечто находится в поле А, а другой - в поле B. Нужно было либо переписать кусочки (чего Света очень боялась, так как они и без того держались на честном слове), либо приделать к каждому кусочку по заплаточке, которая бы сохраняла регистры, пересылала "А" в "B" и так далее.

Света пошла по второму пути и обрела еще одну возможность ошибки - ведь мало того, что неверна была логика кусочка, ошибка могла быть и в заплатке.

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

Еще ее убивало вот что - многие кусочки, с таким трудом отлаженные, вообще не понадобились!

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

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

В конце сентября руководство решило помочь уработавшейся группе и добавило еще одного человека. Руководство считало, что программирование ничем не отличается от копки картошки: если два человека выкопают сотку за столько-то часов, четыре выкопают ее вдвое быстрее.

Новый кадр не знал языка ассемблера и первый раз слышал об информационных системах. Когда-то он написал что-то на ФОРТРАНе операторов в 15, из которых только один был DO. Я думаю, всем стало тут ясно, что это был свежеиспеченный выпускник КГУ. Пока новый кадр изучал ассемблер, пока он понял, что и как делает система (а в этом ему все четверо помогали), наступил ноябрь, и стало ясно, что будь он хоть семи пядей во лбу, ничего сделать не успеет. Тем более, что ненаписанных программ не было, а передавать новому человеку полуотлаженную программу - лучше уж сразу пристрелить его на месте, чтоб не мучился.

Стало ясно, что даже если утроить состав группы, дело быстрее не пойдет. (Потом выяснилось, что это весьма типичная ситуация, и один из матерых программистов, тот самый, что возглавлял разработку ОS/360, сказал об этом так : чтобы выносить ребенка, нужно девять месяцев, независимо от того, сколько женщин вы к этому привлечете).

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

К середине октября Саша тоже достиг отметки в 1000 перфокарт. Причем логика его программы была очень запутанной и gо tо посылал порой метра на три вниз по распечатке, а там выполнялось два-три оператора и новый gо tо посылал его вверх метра на полтора. Саша расстилал распечатку на полу, становился на карачки и - gо tо вперед, gо tо назад, потом опять gо tо назад. Саша уставал настолько, что порой ложился на свое детище, вытягивал ноги и говорил: - только в психбольницу!

Еще он говорил : а чего я буду спешить? Ведь программы, которым я поставляю данные, еще не готовы! На что остальные отвечали : а я что буду спешить - ведь данные не подаются!

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

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

А, пока он в мыле отлаживался, остальные дошли до комплексной отладки. Монитор писать было некогда (кончался ноябрь), и решили оставить тот, простенький, слегка его улучшив. Возможности системы урезались вдвое, но надо было чем-то заткнуть алчущую пасть заказчика.

В декабре приступили к комплексной отладке. Тут-то и обнаружилось, что у Леши, Светы и Наташи разные представления о формате записи. Выяснилось это не сразу, а после месяца препирательств :"Это твоя программа виновата!" - "Нет, твоя! Моя программа работала в этом режиме, значит, это ты неправильные данные подаешь!"

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

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

Наконец, была получена таблица типа 1. Всеобщее ликование!

В процессе ликования мышка неосторожно махнула хвостиком, Наташина программа упала и ... рассыпалась. А так как карты не были пронумерованы (до того ли было!), а многочисленные заплаты не подписаны...

В феврале систему сдали заказчику. Без таблиц типа 2 и документации. Какая уж тут документация!

На пробном прогоне заказчик сказал : "Ребята, что это вы такое сделали? Мы вас просили совсем о другом!"

История эта реальна. Второй программист (Леша) - это я собственной персоной. Система эта не работает до сих пор - мы ее выбросили и написали другую и делали мы ее уже по другому.

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

Оказывается, можно. И об этом мой спецкурс.

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

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

Основная цель спецкурса - привить вам правильное понимание программирования и тех проблем, которые в нем стоят. Я уверен, что вы считаете главным в программировании знание языка и умение переложить на него алгоритм. Это ваша ошибка, и не только ваша.

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

В чем будет главная ваша ошибка, если вы прогуляете половину моего спецкурса, а оставшуюся половину прослушаете вполуха?

Ваша главная ошибка будет состоять в том, что вы, придя на работу, будете считать, что вы занимаетесь тем, что пишете программы.

На самом деле вы будете заниматься совсем другим делом - вы будете заниматься изготовлением программных изделий, и от того, что вы этого не понимаете, жизнь ваша (и потенциальных пользователей ваших программ) имеет большие шансы превратиться в ад.

Запишите, зарубите себе на носу, вызубрите, поймите наконец :

Это -самый главный тезис спецкурса. В зависимости от того, как мы рассматриваем свою деятельность, мы выбираем методы работы. Если мы смотрим на программирование, как на науку, то применяем научные методы управления, научные методы работы. Если мы смотрим на программирование, как на производство, мы применяем промышленные методы. Разница очень большая, сами понимаете.

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

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

Начнем с понятия программы. Пусть вы написали программу сложения пяти двузначных чисел. Она имеет примерно такой вид

(фортран)

dimеnsiоn k(5)
rеаd 1, (k(i), i=1, 5)
1 fоrmаt (5I2)
isum=0
dо 2 i=1, 5
2 isum=isum+k(i)
рrint 3, isum
3 fоrmаt(1Х, I3)
stор
еnd

 

(РL/1)

р:рrос орtiоns(mаin);
dсl к(5);
gеt еdit ((k(i) dо i=1 то 5)) (5 f(2));
isum=0;
dо i=1 то 5;
isum=isum+k(i);
еnd;
рut еdit(isum)(соl(1), f(3));
еnd р;

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

1.Программа может складывать только пять чисел. Если вам понадобится сложить десять чисел, программу придется менять, и существенно :как минимум три карты из десяти. Лучше бы было, если бы программа складывала сколько угодно чисел, т.е. циклы организовать по n, а не по пяти, и соответственно изменить форматы. число n можно ввести, например, первой картой.

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

3.Программа усложнилась, и появились некоторые тонкости. Неясно, например, как должна вести себя программа, если n меньше 1? Или вообще отрицательное - мало ли какие ошибки может допустить тот, кто будет пользоваться вашей программой!

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

4.Теперь получилась весьма универсальная программа, которая складывает сколько угодно произвольных чисел. Жаль, что такой хорошей программой можете пользоваться только вы - ведь инструкции к ней нет! Вот если бы она была, тогда каждый мог бы взять вашу программу и без особых хлопот сложить сколько угодно чисел в произвольном формате.

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

Итак, запишите, как превратить программу в программный продукт.

  1. Написать в как можно более общем виде.
  2. Отладить на различных сочетаниях параметров, в том числе и неверных.
  3. Подробно описать, как с ней работать и как она сама работает.

Сделать все это обычно втрое дороже, чем программу, но и пользы от этого гораздо больше

Итак, мы поняли, что такое программный продукт.

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

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

Все это приводит к тому, что комплекс программ написать как минимум в три раза труднее, чем энное количество программ с такой же суммарной длиной.

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

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

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

Мы рассмотрели эволюцию понятия программы с чисто технической стороны, а теперь посмотрим по другому. О чем вы думаете, когда пишете программу сложения чисел? Правильно, о сумме. Вам нужна сумма, или определитель, или какая-нибудь матрица корреляций, а программа - это нечто промежуточное, это что-то вроде черновиков. Вы их пишете, как хотите - это сугубо ваше дело ; и если кто-то начнет говорить вам, что программу нужно оформлять по ГОСТам, писать, используя определенную технологию , и тому подобное - вы отнесетесь к этому так же, как математик, которого заставляют оформлять по ГОСТам черновики его доказательств, или как крестьянин, которого обязывают изготавливать для себя соху в соответствии с определенной технологией. вы вправе сказать этому кому-то : какое ваше дело? Вот вам сумма, матрица корреляций, теорема, зерно, а какое вам дело до моих программ, черновиков, сохи? Я сделал это для себя, так, как сам хотел и как мне было удобно.

Если провести аналогию с развитием промышленного производства, то мы видим, что налицо все признаки кустарного подхода к производству.

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

Это уже совсем другое программирование, не так ли? И, если сравнивать с развитием промышленности, налицо этап артельной работы по заказам : например, артели, изготавливающие плуги. Уже существуют определенные соглашения между заказчиками и исполнителями, и между исполнителями тоже, уже существует специализация.

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

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

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

Спецкурс наш будет построен следующим образом. Поскольку вы сейчас смотрите на программирование как на процесс записи алгоритмов на языке программирования, то первым делом мне нужно вас в этом разубедить. Мы с вами хорошенько разберем понятие комплексного программного продукта и убедимся в том, что проблем в программировании значительно больше, чем вы сейчас думаете; во всяком случае, та проблема, с которой вы сейчас имеете дело - т.е. изложить первый попавшийся алгоритм решения заданной вам задачи, используя куцое подмножество языка РL/1 (которое вы к тому же плохо знаете) и довести это нечто, что вы называете программой, до чего-то, похожего на результат (или хотя бы добиться того, чтобы это нечто не снималось) - так вот, это еще не проблема, есть множество других, и с ними вы должны познакомиться. Времени у нас немного, и возможно, мы не успеем разобрать все требуемые методы решения этих проблем, но вы хотя бы должны узнать , что эти проблемы вообще существуют.

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

В качестве примера технологии программирования мы рассмотрим Р-технологию, разработанную в институте кибернетики АН УССР. Параллельно спецкурсу будет идти спецсеминар по Р-технологии, с практикой на Р-технологическом комплексе.

На оглавление


© Алексей Бабий 1980-1986