Преподавание программирования — дело почти безнадежное, а его изучение — непосильный труд. Преподаватель может всячески возиться со студентами, читать лекции, делать критические замечания, направлять по верному пути. Студент[3] может все тщательно записывать, запоминать, читать, сдавать зачеты, дискутировать хоть до двух часов ночи. Но все усилия тщетны, если студент не будет практиковаться в написании программ, поскольку навык программирования (как, впрочем, и всякий навык) дается только практикой. Более того, учиться надо на «настоящих» программах, а не на упрощенных примерах, вроде тех, которыми изобилует большинство руководств по языкам программирования. Сколько ни бренчи чижика, вторым Рубинштейном не станешь. Точно так же долбежка языка APL вряд ли поможет вам достичь высот в программировании. Поэтому в настоящей книге представлены довольно объемистые задачи. В качестве учебных проектов они вполне подойдут новичкам, стремящимся стать сначала просто грамотными программистами, а затем и специалистами высокого класса.
Способности, необходимые программисту, можно сравнить с теми, которые требуются, например, очеркисту. Как и очеркист, программист должен владеть некоторыми правилами правописания и грамматики, но, вопреки общепринятому мнению, для них обоих это не главное. Гораздо важнее быть наблюдательным и ищущим, уметь анализировать и ясно выражать свои мысли. Перечислим те способности, которые жизненно необходимы всякому программисту (и очеркисту тоже).
Способность читать и понимать описание поставленной задачи, улавливать пожелания того, кто ее ставит (что не всегда легко, так как и задачи, и те, кто их ставит, часто отличаются именно неуловимостью).
Способность четко видеть действительные трудности и отбрасывать все, не относящееся к делу.
Способность выявлять все случаи, где можно применить теорию, самостоятельно решиться на ее применение или обратиться за советом к специалисту.
Способность разбить задачу на ряд обозримых независимых частей и понять взаимосвязи этих частей.
Способность оценивать эффективность предлагаемых решений с точки зрения затрат на программирование, машинных ресурсов и удовлетворения потребностей пользователя и находить приемлемый компромисс между этими видами эффективности.
Способность объединять множество частных решений воедино, получая при этом четкое и изящное решение всей задачи.
Способность выражать решения на простом и понятном языке. Естественный это язык или искусственный — роли не играет, важно лишь, чтобы правильность решения была ясна и людям, и машине.
И наконец, способность при неудаче подавить самолюбие и поискать другой подход (или даже другую задачу).
Способности эти, как видим, столь сложны и многообразны, что приобрести их можно только на практике. Этюды дают возможность отработать конкретные технические приемы. Накапливая опыт, студент постепенно приобретает качества, необходимые программисту.
Составлять этюды, однако, не так просто, как может показаться. Все еще слишком часто задачки из книжек по программированию представляют собой просто технические «упражнения для пальцев». Полезные для выработки навыков уверенного использования простейших языковых конструкций, они редко бывают «высокохудожественными», что требуется от этюда в определении, приводимом в энциклопедическом словаре. Несмотря на то что этюд — упражнение, «основанное на определенном техническом приеме исполнения» (см. тот же словарь), хороший этюд должен быть достаточно большим, чтобы ощущалась взаимосвязь этого приема с другими областями программирования. Все это наталкивает на мысль взять задачи непосредственно из жизни. «Настоящие» задачи, однако, изобилуют несущественными деталями, требуют обработки массы данных, порождают гору результатов и к тому же меняются чуть ли не каждый день, так как руководство никак не может принять окончательное решение. Из студента, способного освоить профессию прямо в производственном коллективе, конечно, выйдет прекрасный специалист, но слишком многие из обучающихся программированию таким образом не выдерживают и, отчаявшись, бросают. Так что этюд должен лежать где-то посередине между реальной жизнью и тривиальными упражнениями. Две области — игры и информатика — породили, в сущности, почти все эти этюды и наделили их рядом полезных черт. Программисты, как правило, интересуются и тем, и другим приложением (уж лучше бы только информатикой, разумеется). Поскольку культура — всеобщее достояние, большинство игр доступно пониманию каждого; объяснить прикладную задачу в наше время также нетрудно. Очень часто поведение игровой программы или, скажем, транслятора поддается строгому описанию, так что корректность решения можно проверить. Входные данные обычно невелики по объему, и готовить их легко; выходные данные легко воспринимаются. Обе упомянутые области требуют применения весьма развитых алгоритмов и структур данных, так что вряд ли какие-либо сложности в прикладных программах смогут впоследствии поставить студента в тупик. Наконец, в обеих этих областях ЭВМ предстает перед нами как мощный объект абстрактного «разума» (такой подход принят в задачах искусственного интеллекта); возможно, в нашем подборе задач чувствуется давний интерес к «разумным» машинам. Имеется, конечно, много задач и из других прикладных областей. При их отборе мы руководствовались в основном легкостью объяснения ситуации, которая приводит к постановке задачи. Тем, кому некоторые этюды покажутся легкомысленными, мы напомним, что Гайдн создал симфонию из колыбельной песни.
Предполагается, что новичок, берущийся за этюд, уже написал несколько программ и знает сравнительно хорошо хотя бы один язык. Здесь не ставится задача научить конкретным приемам программирования, структурам данных или языкам. Если для решения задачи требуются какие-то специальные знания, трудные места обсуждаются достаточно подробно, а источники дополнительной информации указаны в библиографии. Более того, мы не описываем какой-либо конкретный стиль программирования и не обсуждаем вопросы структурного программирования. Вероятно, большинство читателей слушает лекции или посещает семинарские занятия и может воспользоваться советами преподавателя. Занимающиеся самостоятельно могут почерпнуть сведения по технике и стилю программирования из источников, перечисленных в конце главы.
Каждый этюд распадается на разделы (некоторые из них необязательные). В первом разделе описывается реальная ситуация, во втором — конкретная программа, которую предстоит написать. Обычно ситуация разъясняется достаточно подробно, а постановка задачи — совсем короткая. Затем следует обсуждение трудностей, которые могут встретиться при реализации, и намеки на возможные пути решения. Рассматриваются только существенные моменты. Затем следуют разделы, в которых обсуждается выбор языка и длительность исполнения этюда[4]. Временные оценки, которые рассчитаны на аспирантов первого года обучения, выделяющих для решения задачи четверть своего рабочего времени, могут оказаться малы для программистов, работающих не столь увлеченно. Кроме того, временные оценки могут увеличиваться под влиянием условий доступа к машине. В конце этюда часто содержится расширение поставленной задачи и аннотированная библиография. Решение, найденное с использованием дополнительной литературы, более полезно для студента.
Конечно, результатом работы над этюдом должна быть понятная и четкая программа, стиль и комментарии которой соответствовали бы задаче и выбранному языку. Но этого мало. Еще необходим набор тестов, достаточный для демонстрации работы программы и ее реакции на экстремальные ситуации и неправильное обращение. Наряду с самой программой требуется краткое словесное описание методов решения. Особый упор в нем следует сделать на положенные в основу решения алгоритмы и структуры данных. Наряду с описанием программы программист должен с достаточной степенью правдоподобности хотя бы неформально проиллюстрировать ее правильность (при недостатке времени можно ограничиться рассмотрением ключевых мест). Наконец, должен быть произведен подсчет затраченных ресурсов, как людских, так и машинных; особое внимание следует обратить на обоснование затрат. Также следует указать, чему программист научился на примере этой задачи (на этот вопрос легко ответить, если сформулировать его в виде: «Что я в следующий раз сделаю иначе?»). Такой объем документации может показаться избыточным. Заметим, однако, что умению вовремя поставить точку тоже очень полезно научиться. Решение небольшой задачи не следует перегружать документацией. Один знакомый автору преподаватель определяет оценку на 40% тем, что студент убедил его в правильности программы, на 50% легкостью, с которой его удалось убедить, и только на 10% отличным программированием. Очень хорошая оценка — это 80% и более. А поскольку часть документации — результаты машинных прогонов, такая отметка означает, что программа произвела благоприятное впечатление и на преподавателя, и на ЭВМ.
Первоначально книга предназначалась для студентов — слушателей вводного курса по информатике. Лекционная часть этого курса охватывает широкий спектр вопросов, включая языки и технику программирования, архитектуру ЭВМ, структуры данных, алгоритмы и некоторые сведения из теории. Лектор может использовать некоторые задачи в качестве примеров (скажем, задачу о раскрашивании карты — для обучения Паскалю), но в целом задачи предназначены для самостоятельного решения. Предполагается только, что общее время, отводимое на решение задач, будет не меньше, чем продолжительность всего курса. На структуру самого курса не налагается практически никаких ограничений. С другой стороны, имеются четыре задачи специально для курсов по компиляторам. Эти задачи прямо ориентированы на поддержку обучения методам реализации языков программирования. В нескольких задачах представлены некоторые основные аспекты программирования игр. Другие могут служить материалом для практических занятий по программированию коммерческих задач и задач имитационного моделирования. Заинтересованный преподаватель сможет найти здесь задачи из любой области, кроме численного анализа.
Science Citation Index. Institute for Scientific Information, Philadelphia, PA. Yearly.
Если вы хотите узнать побольше по какому-либо из затронутых в нашей книге направлений, можно воспользоваться цитированной литературой, затем — библиографией из этих работ и т. д. Но как найти работы, которые вышли в свет уже после перечисленных в книге? Если у вас есть некий источник по какой-либо теме, то в Science Citation Index можно найти работы, ссылающиеся на имеющуюся у вас. В каждом из ежегодных выпусков разъясняется, как им пользоваться, да и библиотекарь вам в этом поможет.
Конвей, Грис (Conway R., Gries D.). An Introduction to Programming, 2nd ed. Winthrop, Cambridge, MA, 1975.
Строго говоря, это — введение в программирование (а заодно и хорошее руководство по PL/I). Но, кроме того, это прекрасный учебник по надежности и методам доказательства правильности программ. Перед тем как приступить к вашему первому этюду, имеет смысл повторить материал по построению программ, приведенный в этой книге.
Вирт (Wirth N.). Algorithms + Data Structures = Programs, Prentice-Hall, Englewood Cliffs, NJ, 1976.
Дейкстра (Dijkstra E. W.). A Discipline of Programming, Prentice-Hall, Englewood Cliffs, NJ, 1976. [Имеется перевод: Дейкстра Э. Дисциплина программирования.— М.: Мир, 1978.]
Работы Дейкстры и Вирта перекликаются друг с другом, хотя и написаны независимо. Примерный курс мог бы выглядеть так: прочитайте Конвея и Гриса; попробуйте несколько несложных задач; прочитайте Вирта; попробуйте несколько более трудных задач; прочитайте Дейкстру и снова решите уже пройденные задачи. Вирт, по существу, приводит примеры программ и методы их построения для некоторых задач среднего размера. Дейкстра обсуждает в целом только критические циклы, а также структуры данных, но приводит больше формальных доказательств. В книге Дейкстры также содержатся размышления о программировании как творческой деятельности, и эти мысли, может быть, самая ценная часть книги (но для того, чтобы их оценить, требуется некоторый опыт).
Грисуолд, Поудж, Полонски (Griswold R. E., Poage J. E., Polonsky I. P.). The SNOBOL4 Programming Language, 2nd ed. Prentice-Hall; Englewood Cliffs, NJ, 1971. [Имеется перевод: Грисуолд Р., Поудж Дж., Полонски И. Язык программирования Снобол-4. — М.: Мир, 1980.]
Имеется множество книг по таким языкам, как Фортран, Кобол, Бейсик, Алгол, языки ассемблера и PL/I. Айверсон разработал язык APL как алгоритмический; перед тем как приступить к работе с его конкретной реализацией, ознакомьтесь с соответствующим руководством. Книга Мак-Кимана и др. — эталонное описание языка XPL. Перед тем как работать с языками Лисп или Снобол, очень желательно ознакомиться с особенностями конкретной реализации.
Айверсон (Iverson К. Е.). A Programming Language. Wiley, New York, 1962.
*Гилман, Роуз. Курс АПЛ: диалоговый подход. Пер. с англ. — М.: Мир, 1979.
Йенсен, Вирт (Jensen К., Wirt N.) PASCAL User Manual and Report. Lecture Notes in Computer Science, 18, Springer-Verlag, Berlin, 1974.
*Грогоно. Программирование на языке Паскаль. Пер. с англ. — М.: Мир, 1982.
Кнут (Knuth D. E.). The Art of Computer Programming/Fundamental Algorithms. Addison-Wesley, Reading, MA, 1968. [Имеется перевод: Кнут Д. Искусство программирования для ЭВМ. Т. 1. Основные алгоритмы. — М.: Мир, 1976.]
Серия книг Кнута[5], если он когда-нибудь ее закончит, имеет все шансы стать библией программистов. Конечно же, первый том содержит наиболее элементарные сведения о структурах данных и алгоритмах работы с ними. Если вы не понимаете, как воспользоваться предложенной в настоящей книге структурой данных, — справьтесь у Кнута. Мы, однако, не предлагаем стиль программирования Кнута как образец структурирования программ.
Люка (Lucas F. L.). Style. Collier, New York, 1962.
Эта книга вовсе не о программировании. Вам со временем понадобится писать обширную документацию — тут-то и может помочь эта книга. Более того, многие наблюдения автора применимы также и к написанию программ. Люка сосредоточивает внимание на способах убеждения, а программисту приходится убеждать и машину, и человека.
Мак-Карти и др. (McCarthy J. et al.). LISP 1.5 Programmer's Manual. MIT Press, Cambridge, MA, 1972.
Мак-Киман, Хорнинг, Уортмен (McKeeman W. M., Horning J. J. Wortman D. B.)s A Compiler Generator. Prentice-Hall; Englewood Cliffs, NJ, 1970.
Вегнер (Wegner P.). Programming Languages, Information Structures, and Machine Organization. McGraw-Hill, New York, 1968.
Если у вас возникнут какие-либо вопросы об архитектуре ЭВМ, языках, структурах данных, а также их взаимосвязях, книга Вегнера, возможно, даст ключ к ответу. В книге собрано и увязано воедино исключительное количество распространенных терминов. Приводится краткий обзор информатики и ценный список литературы.