Программирование — страница 42 из 57

Глава 22Идеалы и история

Когда кто-то говорит: “Мне нужен такой язык

программирования, которому достаточно

просто сказать, его я хочу”,

дайте ему леденец.

Алан Перлис (Alan Perlis)


В этой главе очень кратко и выборочно изложена история языков программирования и описаны идеалы, во имя которых они были разработаны. Эти идеалы и выражающие их языки программирования образуют основу профессионализма. Поскольку в настоящей книге используется язык С++, мы сосредоточили свое внимание именно на нем, а также на языках, появившихся под его влиянием. Цель этой главы — изложить основы и перспективы развития идей, представленных в книге. Описывая каждый из языков, мы рассказываем о его создателе или создателях: язык — это не просто абстрактное творение, но и конкретное решение, найденное людьми для стоявших перед ними проблем в определенный момент времени.

22.1. История, идеалы и профессионализм

 История — это чушь”, — безапелляционно заявил Генри Форд (Henry Ford). Противоположное мнение широко цитируется еще с античных времен: “Тот, кто не знает историю, обречен повторить ее”. Проблема заключается в том, чтобы выбрать, какую историю следует знать, а какую следует отбросить: другое известное изречение утверждает, что “95% всей информации — это чушь” (со своей стороны заметим, что 95%, вероятно, являются преуменьшенной оценкой). Наша точка зрения на связь истории с современностью состоит в том, что без понимания истории невозможно стать профессионалом. Люди, очень мало знающие предысторию своей области знаний, как правило, являются легковерными, поскольку история любого предмета замусорена правдоподобными, но не работоспособными идеями. “Плоть” истории состоит из идей, ценность которых доказывается практикой.

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

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

22.1.1. Цели и философия языка программирования

Что такое язык программирования? Для чего он предназначен? Ниже приводятся распространенные варианты ответа на первый вопрос.

• Инструмент для инструктирования машин.

• Способ записи алгоритмов.

• Средство общения программистов.

• Инструмент для экспериментирования.

• Средство управления компьютеризированными устройствами.

• Способ выражения отношения между понятиями.

• Средство выражения проектных решений высокого уровня.


Наш ответ таков: “Все вместе и еще больше!” Очевидно, что здесь речь идет об универсальных языках программирования. Кроме них существуют специализированные и предметно-ориентированные языки программирования, предназначенные для более узких и более точно сформулированных задач. Какие свойства языка программирования считаются желательными?

• Переносимость.

• Типовая безопасность.

•Точная определенность.

• Высокая производительность.

• Способность точно выражать идеи.

• Легкая отладка.

• Легкое тестирование.

• Доступ ко всем системным ресурсам.

• Независимость от платформы.

• Возможность выполнения на всех платформах.

• Устойчивость на протяжении десятилетий.

• Постоянное совершенствование в ответ на изменения, происходящие в прикладной области.

• Легкость обучения.

• Небольшой размер.

• Поддержка популярных стилей программирования (например, объектно-ориентированного и обобщенного программирования).

• Возможность анализа программ.

• Множество возможностей.

• Поддержка со стороны крупного сообщества.

• Поддержка со стороны новичков (студентов, учащихся).

• Исчерпывающие возможности для экспертов (например, конструкторов инфраструктуры).

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

• Доступность большого количества компонентов программного обеспечения (например, библиотек).

• Поддержка со стороны сообщества разработчиков открытого кода.

• Поддержка со стороны поставщиков основных платформ (Microsoft, IBM и т.д.).


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

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

22.1.2. Идеалы программирования

Предисловие к книге The C++ Programming Language начинается со слов: “Язык C++ — универсальный язык программирования, разработанный для того, чтобы серьезные программисты получали удовольствие от работы”. Что это значит? Разве программирование не предусматривает поставку готовой продукции? А почему ничего не сказано о правильности, качестве и сопровождении программ? А почему не упомянуто время от начального замысла новой программы до ее появления на рынке? А разве поддержка разработки программного обеспечения не важна? Все это, разумеется, тоже важно, но мы не должны забывать о программисте. Рассмотрим другой пример. Дональд Кнут (Don Knuth) сказал: “Самое лучшее в компьютере Alto то, что он ночью не работает быстрее”. Alto — это компьютер из центра Xerox Palo Alto Research Center (PARC), бывший одним из первых персональных компьютеров. Он отличался от обычных компьютеров, предназначенных для совместного использования и провоцировавших острое соперничество между программистами за дневное время работы.

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

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

В оставшейся части раздела мы рассмотрим следующие вопросы.

• Что мы хотим от кода?

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

• Ключевые аспекты структуры программ, выраженные в коде.

• Непосредственное выражение идей.

• Уровень абстракции.

• Модульность.

• Логичность и минимализм.


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

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

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

22.1.2.1. Чего мы хотим?

 Как правило, мы хотим следующего.

Правильность. Да, очень трудно определить, что мы имеем в виду под словом “правильный”, но это важная часть работы. Часто это понятие в рамках конкретного проекта определяют для нас другие люди, но в этом случае мы должны интерпретировать то, что они говорят.

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

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

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


Все сказанное стимулирует наш интерес к структуре кода.

• Если в программе есть ошибка (каждая большая программа содержит ошибки), то найти ее легче, если программа имеет четкую структуру.

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

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


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

22.1.2.2. Общие подходы

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

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

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


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

• Мы не можем создать и проверить основные компоненты, заранее устранив все источники ошибок.

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


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

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

22.1.2.3. Непосредственное выражение идей

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

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

Month
или
Color
), а не общего (например,
int
).

Независимое представление в коде независимых идей. Например, за некоторым исключением, стандартная функция

sort()
может упорядочивать любой стандартный контейнер любого элементарного типа; концепции сортировки, критерии сортировки контейнера и элементарный тип являются независимыми понятиями. Если бы мы должны были создать вектор объектов, расположенных в свободной памяти, элементы которого относятся к классу, выведенному из класса
Object
с функцией-членом
before()
, определенной для вызова из функции
vector::sort()
, то должны были бы иметь более узкую версию функции
sort()
, поскольку сделали предположения о хранении, иерархии классов, доступных функциях-членах, порядке и т.д.

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

Circle
является разновидностью класса
Shape
) и параметризация (например, класс
vector
выражает нечто общее для всех векторов независимо от типа элементов).

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

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

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

sort(b,e,op)
, сортирующей элементы с помощью оператора
op
, существует вариант
sort(b,e)
, выполняющий неявную сортировку с помощью отношения “меньше”. Если бы мы могли (или имели возможность использовать язык C++0x; см. раздел 22.2.6), то предусмотрели бы также версию
sort(c)
для сортировки стандартного контейнера с помощью отношения “меньше” и функцию
sort(c,op)
для сортировки стандартного контейнера с помощью оператора
op
.

22.1.2.4. Уровень абстракции

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

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

vector>
. Однако если мы почти всегда обращаемся к этому множеству для поиска имени, то более высокий уровень абстракции обеспечит нам класс
map
. Это позволит не писать (и отлаживать) функции доступа к записям. С другой стороны, класс
vector>
сам по себе находится на более высоком уровне абстракции, чем два массива,
string[max]
и
Value_type[max]
, где отношение между строкой и значением носит неявный характер. На самом низком уровне абстракции могло бы находиться сочетание типа
int
(количество элементов) и двух указателей
void*
(ссылающихся на какую-то форму записи, известную программисту, но не компилятору). В нашем примере каждое из предложенных решений можно отнести к низкому уровню абстракции, поскольку в каждом из них основное внимание сосредоточено на представлении пар значений, а не на их функциях. Для того чтобы приблизиться к реальному приложению, следует определить класс, который непосредственно отражает способ его использования. Например, мы могли бы написать код приложения, используя класс
Phonebook
с удобным интерфейсом. Класс
Phonebook
можно было бы реализовать с помощью одного из описанных выше представлений данных.

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

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

Phonebook
, можем выбрать способ его реализации, например, в виде сочетания массивов
string[max]
и
Value_type[max]
или в виде класса
map
. Для одних приложений более эффективным оказывается первый вариант, а для других — второй. Естественно, производительность не является основным фактором, если вы пишете программу для хранения записей из своей телефонной книжки. Но оно становится существенным, если необходимо хранить и обрабатывать миллионы записей. Что еще важнее, использование низкоуровневых средств сопряжено с затратами рабочего времени, которого программисту не хватит на усовершенствование (повышение производительности или чего-то другого).

22.1.2.5. Модульность

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

 Модульность кода должна отражать основные логические разделы приложения. Не следует повышать степень повторного использования, просто погружая два совершенно разных класса А и В в повторно используемый компонент C. Объединение интерфейсов классов A и B в новом модуле C усложняет код.



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

Чем можно помочь? Может быть, следует создать общий интерфейс классов А и В?



Эти диаграммы подсказывают, что следует использовать наследование и параметризацию соответственно. В обоих случаях, для того чтобы работа имела смысл, интерфейс должен быть меньше, чем простое объединение интерфейсов классов А и В. Иначе говоря, для того чтобы пользователь получил выгоду от принятого решения, классы А и В должны иметь фундаментальную общность. Обратите внимание на то, что мы снова вернулись к интерфейсам (см. разделы 9.7 и 25.4.2) и, как следствие, к инвариантам (см. раздел 9.4.3).

22.1.2.6. Логичность и минимализм

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

• Не добавляйте свойство, если сомневаетесь в его необходимости.

• Похожие свойства должны иметь похожие интерфейсы (и имена), но только если их сходство носит фундаментальный характер.

• Непохожие свойства должны иметь непохожие имена (и по возможности разные интерфейсы), но только если их различие носит фундаментальный характер


Логичное именование, стиль интерфейса и стиль реализации облегчают эксплуатацию программы. Если код логичен, то программист не будет вынужден изучать новый набор соглашений, касающихся каждой части крупной системы. Примером является библиотека STL (см. главы 20-21, раздел Б.4–6). Если обеспечить логичность не удается (например, из-за наличия старого кода или кода, написанного на другом языке), то целесообразно создать интерфейс, который обеспечит согласование стиля с остальной частью программы. В противном случае этот чужеродный (“странный”, “плохой”) код “заразит” каждую часть программы, вынужденную к нему обращаться.

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

 Некоторые программисты придерживаются принципа проектирования KISS (“Keep It Simple, Stupid” — “Делай проще, тупица”). Нам даже доводилось слышать, что принцип KISS — единственный стоящий принцип проектирования. Однако мы предпочитаем менее вызывающие формулировки, например “Keep simple things simple” (“Не усложняй простые вещи”) и “Keep it simple: as simple as possible, but no simpler” (“Все должно быть как можно более простым, но не проще”). Последнее высказывание принадлежит Альберту Эйнштейну (Albert Einstein). Оно подчеркивает опасность чрезмерного упрощения, выходящего за рамки здравого смысла и разрушающего проект. Возникает очевидный вопрос: “Просто для кого и по сравнению с чем?”

22.1.3. Стили и парадигмы

 Когда мы проектируем и реализуем программы, мы должны придерживаться последовательного стиля. Язык С++ поддерживает четыре главных стиля, которые можно считать фундаментальными.

• Процедурное программирование.

• Абстракция данных.

• Объектно-ориентированное программирование.

• Обобщенное программирование.


Иногда их называют (несколько помпезно) парадигмами программирования. Существует еще несколько парадигм, например: функциональное программирование (functional programming), логическое программирование (logic programming), продукционное программирование (rule-based programming), программирование в ограничениях (constraints-based programming) и аспектно-ориентированное программирование (aspect-oriented programming). Однако язык С++ не поддерживает эти парадигмы непосредственно, и мы не можем охватить их в одной книге, поэтому откладываем эти вопросы на будущее.

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

sqrt()
и
cos()
. В языке С++ этот стиль программирования основан на использовании функций (см. главу 8). Вероятно, самой ценной является возможность выбирать механизм передачи аргументов по значению, по ссылке и по константной ссылке. Часто данные организовываются в структуры с помощью конструкций
struct
. Явные механизмы абстракции (например, закрытые данные-члены и функции-члены класса не используются). Отметим, что этот стиль программирования — и функции — является интегральной частью любого другого стиля.

Абстракция данных. Основная идея этой парадигмы — сначала создать набор типов для предметной области, а затем писать программы для их использования. Классическим примером являются матрицы (разделы 24.3–24.6). Интенсивно используется явное сокрытие данных (например, использование закрытых членов класса). Распространенными примерами абстракции данных являются стандартные классы

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

Объектно-ориентированное программирование. Основная идея этой парадигмы программирования — организовать типы в иерархии, чтобы выразить их отношения непосредственно в коде. Классический пример — иерархия Shape, описанная в главе 14. Этот подход имеет очевидную ценность, когда типы действительно имеют иерархические взаимоотношения. Однако существует сильная тенденция к его избыточному применению; иначе говоря, люди создают иерархии типов, не имея на это фундаментальных причин. Если люди создают производные типы, то задайте вопрос: “Зачем?” Что выражает это выведение? Чем различие между базовым и производным классом может мне помочь в данном конкретном случае?

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

high()
, описанная в главе 20. Алгоритмы
find()
и
sort()
из библиотеки являются классическими алгоритмами поиска и сортировки, выраженными в очень общей форме с помощью обобщенного программирования. См. также примеры в главах 20-21.


 Итак, подведем итоги! Часто люди говорят о стилях программирования (парадигмах) так, будто они представляют собой противоречащие друг другу альтернативы: либо вы используете обобщенное программирование, либо объектно-ориентированное. Если хотите выразить решения задач наилучшим образом, то используйте комбинацию этих стилей. Выражение “наилучшим образом” означает, что вашу программу легко читать, писать, легко эксплуатировать и при этом она достаточно эффективна.

Рассмотрим пример: классический класс

Shape
, возникший в языке Simula (раздел 22.2.4), который обычно считается воплощением объектно-ориентированного программирования. Первое решение может выглядеть так:


void draw_all(vector& v)

{

  for(int i = 0; idraw();

}


Этот фрагмент кода действительно выглядит “довольно объектно-ориентированным”. Он основан на иерархии классов и вызове виртуальной функции, при котором правильная функция

draw()
для каждого конкретного объекта класса
Shape
находится автоматически; иначе говоря, для объекта класса
Circle
он вызовет функцию
Circle::draw()
, а для объекта класса
Open_polyline
— функцию
Open_polyline::draw()
. Однако класс
vector
по существу является конструктивным элементом обобщенного программирования: он использует параметр (тип элемента), который выясняется на этапе компиляции. Следует подчеркнуть, что для итерации по всем элементам используется алгоритм из стандартной библиотеки.


void draw_all(vector& v)

{

  for_each(v.begin(),v.end(),mem_fun(&Shape::draw));

}


Третьим аргументом функции

for_each()
является функция, которая должна вызываться для каждого элемента последовательности, заданной двумя первыми аргументами (раздел Б.5.1). Предполагается, что третья функция представляет собой обычную функцию (или функцию-объект), которая вызывается с помощью синтаксической конструкции
f(x)
, а не функцию-член, вызываемую с помощью синтаксической конструкции
p–>f()
. Следовательно, для того чтобы указать, что на самом деле мы хотим вызвать функцию-член (виртуальную функцию
Shape::draw()
), необходимо использовать стандартную библиотечную функцию
mem_fun()
(раздел Б.6.2). Дело в том, что функции
for_each()
и
mem_fun()
, будучи шаблонными, на самом деле не очень хорошо соответствуют объектно-ориентированной парадигме; они полностью относятся к обобщенному программированию. Еще интереснее то, что функция
mem_fun()
является автономной (шаблонной) функцией, возвращающей объект класса. Другими словами, ее следует отнести к простой абстракции данных (нет наследования) или даже к процедурному программированию (нет сокрытия данных). Итак, мы можем констатировать, что всего лишь одна строка кода использует все четыре фундаментальных стиля программирования, поддерживаемых языком C++.

 Зачем же мы написали вторую версию примера для рисования всех фигур? По существу, она не отличается от первой, к тому же на несколько символов длиннее! В свое оправдание укажем, что выражение концепции цикла с помощью функции

for_each()
является более очевидным и менее уязвимым для ошибок, чем цикл
for
, но для многих этот аргумент не является очень убедительным. Лучше сказать, что функция
for_each()
выражает то, что мы хотим сделать (пройти по последовательности), а не как мы это хотим сделать. Однако для большинства людей достаточно просто сказать: “Это полезно”. Такая запись демонстрирует путь обобщения (в лучших традициях обобщенного программирования), позволяющий устранить много проблем. Почему все фигуры хранятся в векторе, а не в списке или в обобщенной последовательности? Следовательно, мы можем написать третью (более общую) версию.


template void draw_all(Iter b, Iter e)

{

  for_each(b,e,mem_fun(&Shape::draw));

}


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

Shape
.


Point p(0,100);

Point p2(50,50);

Shape* a[] = { new Circle(p,50), new Triangle(p,p2,Point(25,25)) };

draw_all(a,a+2);


За неимением лучшего термина мы называем программирование, использующее смесь наиболее удобных стилей, мультипарадигменным (multi-paradigm programming).

22.2. Обзор истории языков программирования

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

 Существует много языков программирования. Они появляются со скоростью примерно 2000 языков за десять лет, впрочем скорость их исчезновения примерно такая же. В этом разделе мы вспомним о десяти языках, изобретенных за последние почти шестьдесят лет. Более подробную информацию можно найти на веб-странице http://research.ihost.com/hopl/HOPL.html, там же имеются ссылки на все статьи, опубликованные на трех конференциях ACM SIGPLAN HOPL (History of Programming Languages — история языков программирования). Эти статьи прошли строгое рецензирование, а значит, они более полны и достоверны, чем среднестатистические источники информации в сети веб. Все языки, которые мы обсудим, были представлены на конференциях HOPL. Набрав полное название статьи в поисковой веб-машине, вы легко ее найдете. Кроме того, большинство специалистов по компьютерным наукам, упомянутых в этом разделе, имеют домашние страницы, на которых можно найти больше информации об их работе.

Мы вынуждены приводить только очень краткое описание языков в этой главе, ведь каждый упомянутый язык (и сотни не упомянутых) заслуживает отдельной книги. В каждом языке мы выбрали только самое главное. Надеемся, что читатели воспримут это как приглашение к самостоятельному поиску, а не подумают: “Вот и все, что можно сказать о языке Х!”. Напомним, что каждый упомянутый здесь язык был в свое время большим достижением и внес важный вклад в программирование. Просто из-за недостатка места мы не в состоянии отдать этим языкам должное, но не упомянуть о них было бы совсем несправедливо. Мы хотели бы также привести несколько строк кода на каждом из этих языков, но, к сожалению, для этого не хватило места (см. упр. 5 и 6).

Слишком часто об артефактах (например, о языках программирования) говорят лишь, что они собой представляют, или как о результатах анонимного процесса разработки. Это неправильное изложение истории: как правило, особенно на первых этапах, на язык влияют идеи, место работы, личные вкусы и внешние ограничения одного человека или (чаще всего) нескольких людей. Таким образом, за каждым языком стоят конкретные люди. Ни компании IBM и Bell Labs, ни Cambridge University, ни другие организации не разрабатывают языки программирования, их изобретают люди, работающие в этих организациях, обычно в сотрудничестве со своими друзьями и коллегами.

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

22.2.1. Первые языки программирования

 Когда в 1949 году появились первые электронные компьютеры, позволяющие хранить программы, каждый из них имел свой собственный язык программирования. Существовало взаимно однозначное соответствие между выражением алгоритма (например, вычисления орбиты планеты) и инструкциями для конкретной машины. Очевидно, что ученый (пользователями чаще всего были ученые) писали математические формулы, но программа представляла собой список машинных инструкций. Первые примитивные списки состояли из десятичных или восьмеричных цифр, точно соответствовавших их представлению в машинной памяти. Позднее появился ассемблер и “автокоды”; иначе говоря, люди разработали языки, в которых машинные инструкции и средства (например, регистры) имели символьные имена. Итак, программист мог написать “LD R0 123”, чтобы загрузить содержимое памяти, расположенной по адресу 123, в регистр 0. Однако каждая машина по-прежнему имела свой собственный набор инструкций и свой собственный язык программирования.



Ярким представителем разработчиков языков программирования в то время является, несомненно, Дэвид Уилер (David Wheeler) из компьютерной лаборатории Кембриджского университета (University of Cambridge Computer Laboratory). В 1948 году он написал первую реальную программу, которая когда-либо была выполнена на компьютере, хранившем программы в своей памяти (программа, вычислявшая таблицу квадратов; см. раздел 4.4.2.1). Он был одним из десяти людей, объявивших о создании первого компилятора (для машинно-зависимого автокода). Он изобрел вызов функции (да, даже такое очевидное и простое понятие было когда-то изобретено впервые). В 1951 году он написал блестящую статью о разработке библиотек, которая на двадцать лет опередила свое время! В соавторстве с Морисом Уилксом (Maurice Wilkes) (см. выше) и Стенли Гиллом (Stanley Gill) он написал первую книгу о программировании. Он получил первую степень доктора философии в области компьютерных наук (в Кембриджском университете в 1951 году), а позднее внес большой вклад в развитие аппаратного обеспечения (кэш-архитектура и ранние локальные сети) и алгоритмов (например, алгоритм шифрования TEA (см. раздел 25.5.6) и преобразование Бэрроуза–Уилера (Burrows-Wheeler transform) — алгоритм сжатия, использованный в архиваторе bzip2). Дэвид Уилер стал научным руководителем докторской диссертации Бьярне Страуструпа (Bjarne Stroustrup). Как видите, компьютерные науки — молодая дисциплина. Дэвид Уилер выполнил большую часть своей выдающейся работы, будучи еще аспирантом. Впоследствии он стал профессором Кембриджского университета и членом Королевского общества (Fellow of the Royal Society).


Ссылки

Burrows, M., and David Wheeler. “A Block Sorting Lossless Data Compression Algorithm.” Technical Report 124, Digital Equipment Corporation, 1994.

Bzip2 link: www.bzip.org.

Cambridge Ring website: http://koo.corpus.cam.ac.uk/projects/earlyatm/ cr82.

Campbell-Kelly, Martin. “David John Wheeler.”Biographical Memoirs of Fellows of the Royal Society, Vol. 52, 2006. (Его формальная биография.)

EDSAC: http://en.wikipedia.org/wiki/EDSAC.

Knuth, Donald. The Art of Computer Programming. Addison-Wesley, 1968, and many revisions. Look for “David Wheeler” in the index of each volume.

TEA link: http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm.

Wheeler, D. J. “The Use of Sub-routines in Programmes.” Proceedings of the 1952 ACM National Meeting. (Это библиотека технических отчетов, начиная с 1951 года.)

Wilkes, M. V., D. Wheeler, and S. Gill. Preparation of Programs for an Electronic Digital Computer. Addison-Wesley Press, 1951; 2nd edition, 1957. Первая книга о программировании.

22.2.2. Корни современных языков программирования

Ниже приведена диаграмма важнейших первых языков.



Важность этих языков частично объясняется тем, что они широко используются (а в некоторых случаях используются и ныне), а частично тем, что они стали предшественниками важных современных языков, причем часто наследники имели те же имена. Этот раздел посвящен трем ранним языкам — Fortran, COBOL и Lisp, — ставшим прародителями большинства современных языков программирования.

22.2.2.1. Язык программирования Fortran

 Появление языка Fortran в 1956 году, вероятно, является наиболее значительным событием в истории языков программирования. Fortran — это сокращение словосочетания “Formula Translation”[9]. Его основная идея заключалась в генерации эффективного машинного кода, ориентированного на людей, а не на машины. Система обозначений, принятая в языке Fortran, напоминала систему, которой пользовались ученые и инженеры, решающие математические задачи, а не машинные инструкции (тогда лишь недавно появившиеся) электронных компьютеров.

С современной точки зрения язык Fortran можно рассматривать как первую попытку непосредственного представления предметной области в коде. Он позволял программистам выполнять операции линейной алгебры точно так, как они описаны в учебниках. В языке Fortran есть массивы, циклы и стандартные математические формулы (использующие стандартные математические обозначения, такие как x+y и sin(x)). Язык содержал стандартную библиотеку математических функций, механизмы ввода-вывода, причем пользователь мог самостоятельно определять дополнительные функции и библиотеки.

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

Считалось важным, чтобы машинный код, сгенерированный на основе исходного кода, написанного на языке Fortran, был как можно ближе к оптимальному с точки зрения эффективности: машины были огромными и чрезвычайно дорогими (во много раз больше зарплаты коллектива программистов), удивительно (по современным меркам) медленными (около 100 тыс. операций в секунду) и имели абсурдно малую память (8 K). Однако люди умудрялись втискивать в эти машины полезные программы, и это ограничивало применение улучшенной системы обозначений (ведущее к повышению производительности работы программиста и усилению переносимости программ).

Язык Fortran пользовался огромным успехом в области научных и инженерных вычислений, для которых он собственно и предназначался. С момента своего появления он постоянно эволюционировал. Основными версиями языка Fortran являются версии II, IV, 77, 90, 95 и 03, причем до сих пор продолжаются споры о том, какой из языков сегодня используется чаще: Fortran77 или Fortran90.

Первое определение и реализация языка Fortran были выполнены коллективом сотрудников компании IBM под руководством Джона Бэкуса (John Backus): “Мы не знали, чего хотели и как это сделать. Язык просто вырастал”. Что они могли знать? До сих пор никто ничего подобного не делал, но постепенно они разработали или открыли основную структуру компилятора: лексический, синтаксический и семантический анализ, а также оптимизацию. И по сей день язык Fortran является лидером в области оптимизации математических вычислений. Среди открытий, появившихся после языка Fortran, была система обозначений для специальной грамматики: форма Бэкуса–Наура (Backus-Naur Form — BNF). Впервые она была использована в языке Algol-60 (см. раздел 22.2.3.1) и в настоящее время используется в большинстве современных языков. Мы использовали вариант формы BNF в нашей грамматике, описанной в главах 6 и 7.



 Много позже Джон Бэкус стал основоположником новой области языков программирования (функционального программирования), опирающейся на математический подход к программированию в отличие от машинно-ориентированного подхода, основанного на чтении и записи содержимого ячеек памяти. Следует подчеркнуть, что в чистой математике нет понятия присваивания и даже оператора. Вместо этого вы “просто” указываете, что должно быть истинным в определенных условиях. Некоторые корни функционального программирования уходят в язык Lisp (см. раздел 22.2.2.3), а другие идеи функционального программирования отражены в библиотеке STL (см. главу 21).


Ссылки

Backus, John. “Can Programming Be Liberated from the von Neumann Style?” Communications of the ACM, 1977. (Его лекция по случаю присуждения премии Тьюринга.)

Backus, John. “The History of FORTRAN I, II, and III.” ACM SIGPLAN Notices, Vol. 13 No. 8, 1978. Special Issue: History of Programming Languages Conference.

Hutton, Graham. Programming in Haskell. Cambridge University Press, 2007. ISBN 0521692695.

ISO/IEC 1539. Programming Languages — Fortran. (The “Fortran 95” standard.)

Paulson, L. C. ML for the Working Programmer. Cambridge University Press, 1991. ISBN 0521390222.

22.2.2.2. Язык программирования COBOL

Для программистов, решающих задачи, связанные с бизнесом, язык COBOL (Common Business-Oriented Language — язык программирования для коммерческих и деловых задач) был (и кое-где остается до сих пор) тем, чем язык Fortran был (и кое-где остается до сих пор) для программистов, проводящих научные вычисления. Основной упор в этом языке сделан на манипуляции данными.

• Копирование.

• Хранение и поиск (хранение записей).

• Вывод на печать (отчеты).


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

Изначально язык COBOL был разработан комитетом CODASYL в 1959-60 годах по инициативе Министерства обороны США (U.S. Department of Defense) и группы основных производителей компьютеров для выполнения вычислений, связанных с деловыми и коммерческими задачами. Проект был основан на языке FLOW-MATIC, изобретенным Грейс Хоппер. Одним из ее вкладов в разработку языка было использование синтаксиса, близкого к английскому языку (в отличие от математических обозначений, принятых в языке Fortran и доминирующих до сих пор). Как и язык Fortran, а также все успешные языки программирования, COBOL претерпевал непрерывные изменения. Основными версиями были 60, 61, 65, 68, 70, 80, 90 и 04.

Грейс Мюррей Хоппер (Grace Murray Hopper) имела степень доктора философии по математике, полученную в Йельском университете (Yale University). Во время Второй мировой войны она работала на военно-морской флот США на самых первых компьютерах. Через несколько лет, проведенных в только что возникшей компьютерной промышленности, она вернулась на службу в военно-морской флот.


“Контр-адмирал доктор Грейс Мюррей Хоппер (Военно-морской флот США) была замечательной женщиной, достигших грандиозных результатов в программировании на первых компьютерах. На протяжении всей своей жизни она была лидером в области разработки концепций проектирования программного обеспечения и внесла большой вклад в переход от примитивных методов программирования к использованию сложных компиляторов. Она верила, что лозунг “мы всегда так делали” не всегда является хорошим основанием для того, чтобы ничего не менять”.

Анита Борг (Anita Borg) из выступления на конференции

“Grace Hopper Celebration of Women in Computing”, 1994



Грейс Мюррей Хоппер часто называют первой, кто назвал ошибку в компьютере “жучком” (bug). Безусловно, она была одной из первых, кто использовал этот термин и подтвердил это документально.



Жучок был реальным (молью) и повлиял на аппаратное обеспечение самым непосредственным образом. Большинство современных “жучков” гнездятся в программном обеспечении и внешне выглядят не так эффектно.


Ссылки

Биография Г. М. Хоппер: http://tergestesoft.com/~eddysworld/hopper.htm. ISO/IEC 1989:2002. Information Technology — Programming Languages — COBOL.

Sammet, Jean E. “The Early History of COBOL.” ACM SIGPLAN Notices, Vol. 13, No. 8,

1978. Special Issue: History of Programming Languages Conference.

22.2.2.3. Язык программирования Lisp

Язык Lisp был разработан в 1958 году Джоном Маккарти (John McCarthy) из Массачусетского технологического института (MIT) для обработки связанных списков и символьной информации (этим объясняется его название: LISt Processing). Изначально язык Lisp интерпретировался, а не компилировался (во многих случаях это положение не изменилось и в настоящее время). Существуют десятки (а вероятнее всего, сотни) диалектов языка Lisp. Часто говорят, что язык Lisp подразумевает разнообразные реализации. В данный момент наиболее популярными диалектами являются языки Common Lisp и Scheme.

Это семейство языков было и остается опорой исследований в области искусственного интеллекта (хотя поставляемые программные продукты часто написаны на языке C или C++). Одним из основных источников вдохновения для создателей языка Lisp было лямбда-исчисление (точнее, его математическое описание).

Языки Fortran и COBOL были специально разработаны для устранения реальных проблем в соответствующих предметных областях. Разработчики и пользователи языка Lisp больше интересовались собственно программированием и элегантностью программ. Часто их усилия приводили к успеху. Язык Lisp был первым языком, не зависевшим от аппаратного обеспечения, причем его семантика имела математическую форму. В настоящее время трудно точно определить область применения языка Lisp: искусственный интеллект и символьные вычисления нельзя спроектировать на реальные задачи так четко, как это можно сделать для деловых вычислений или научного программирования. Идеи языка Lisp (и сообщества разработчиков и пользователей языка Lisp) можно обнаружить во многих современных языках программирования, особенно в функциональных языках.



Джон Маккарти получил степень бакалавра по математике в Калифорнийском технологическом институте (California Institute of Technology), а степень доктора философии по математике — в Принстонском университете (Princeton University). Следует подчеркнуть, что среди разработчиков языков программирования много математиков. После периода плодотворной работы в MIT в 1962 году Маккарти переехал в Станфорд, чтобы участвовать в основании лаборатории по изучению искусственного интеллекта (Stanford AI lab). Ему приписывают изобретение термина “искусственный интеллект” (artificial intelligence), а также множество достижений в этой области.


Ссылки

Abelson, Harold, and Gerald J. Sussman. Structure and Interpretation of Computer Programs, Second Edition. MIT Press, 1996. ISBN 0262011530.

ANSI INCITS 226-1994 (formerly ANSI X3.226:1994). American National Standard for Programming Language — Common LISP.

McCarthy, John. “History of LISP.” ACM SIGPLAN Notices, Vol. 13 No. 8, 1978.

Special Issue: History of Programming Languages Conference.

Steele, Guy L. Jr. Common Lisp: The Language. Digital Press, 1990. ISBN 1555580416.

Steele, Guy L. Jr., and Richard Gabriel. “The Evolution of Lisp”. Proceedings of the ACM History of Programming Languages Conference (HOPL-2). ACM SIGPLAN Notices, Vol. 28 No. 3, 1993.

22.2.3. Семейство языков Algol

В конце 1950-х годов многие почувствовали, что программирование стало слишком сложным, специализированным и слишком ненаучным. Возникло убеждение, что языки программирования излишне разнообразны и что их следует объединить в один язык без больших потерь для общности на основе фундаментальных принципов. Эта идея носилась в воздухе, когда группа людей собралась вместе под эгидой IFIP (International Federation of Information Processing — Международная федерация по обработке информации) и всего за несколько лет создала новый язык, который совершил революцию в области программирования. Большинство современных языков, включая язык С++, обязаны своим существованием этому проекту.

22.2.3.1. Язык программирования Algol-60

 “Алгоритмический язык” (“ALGOrithmic Language” — Algol), ставший результатом работы группы IFIP 2.1, открыл новые концепции современных языков программирования.

• Контекст лексического анализа.

• Использование грамматики для определения языка.

• Четкое разделение синтаксических и семантических правил.

• Четкое разделение определения языка и его реализации.

• Систематическое использование типов (статических, т.е. на этапе компиляции).

• Непосредственная поддержка структурного программирования.


Само понятие “универсальный язык программирования” пришло вместе с языком Algol. До того времени языки предназначались для научных вычислений (например, Fortran), деловых расчетов (например, COBOL), обработки списков (например, Lisp), моделирования и т.д. Из всех перечисленных язык Algol-60 ближе всего к языку Fortran.

К сожалению, язык Algol-60 никогда не вышел за пределы академической среды. Многим он казался слишком странным. Программисты, предпочитавшие Fortran, утверждали, что программы на Algol-60 работают слишком медленно, программисты, работавшие на языке Cobol, говорили, что Algol-60 не поддерживает обработку деловой информации, программисты, работавшие на языке Lisp, говорили, что Algol-60 недостаточно гибок, большинство остальных людей (включая менеджеров, управляющих инвестициями в разработку программного обеспечения) считали его слишком академичным, и, наконец, многие американцы называли его слишком европейским. Большинство критических замечаний было справедливым. Например, в отчете о языке Algol-60 не был определен ни один механизм ввода-вывода! Однако эти замечания можно адресовать большинству современных языков программирования, — ведь именно язык Algol установил новые стандарты во многих областях программирования.

Главная проблема, связанная с языком Algol-60, заключалась в том, что никто не знал, как его реализовать. Эта проблема была решена группой программистов под руководством Питера Наура (Peter Naur), редактора отчета по языку Algol-60 и Эдсгером Дейкстрой (Edsger Dijkstra).



Питер Наур получил образование астронома в Копенгагенском университете (University of Copenhagen) и работал в Техническом университете Копенгагена (Technical University of Copenhagen — DTH), а также на датскую компанию Regnecentralen, производившую компьютеры. Программирование он изучал в 1950–1951 годы в компьютерной лаборатории в Кембридже (Computer Laboratory in Cambridge), поскольку в то время в Дании не было компьютеров, а позднее сделал блестящую академическую и производственную карьеру. Он был одним из авторов создания формы BNF (Backus-Naur Form — форма Бэкуса–Наура), использовавшейся для описания грамматики, а также одним из первых поборников формальных рассуждений о программах (Бьярне Страуструп впервые — приблизительно в 1971 году — узнал об использовании инвариантов из технических статей Питера Наура). Наур последовательно придерживался вдумчивого подхода к вычислениям, всегда учитывая человеческий фактор в программировании. Его поздние работы носили философский характер (хотя он считал традиционную академическую философию совершенной чепухой). Он стал первым профессором даталогии в Копенгагенском университете (датский термин “даталогия” (datalogi) точнее всего переводится как “информатика”; Питер Наур ненавидел термин “компьютерные науки” (computer scienses), считая его абсолютно неправильным, так как вычисления — это не наука о компьютерах).



Эдсгер Дейкстра (Edsger Dijkstra) — еще один великий ученый в области компьютерных наук. Он изучал физику в Лейдене, но свои первые работы выполнил в Математическом центре (Mathematisch Centrum) в Амстердаме. Позднее он работал в нескольких местах, включая Эйндховенский технологический университет (Eindhoven University of Technology), компанию Burroughs Corporation и университет Техаса в Остине (University of Texas (Austin)). Кроме плодотворной работы над языком Algol, он стал пионером и горячим сторонником использования математической логики в программировании и теории алгоритмов, а также одним из разработчиков и конструкторов операционной системы ТНЕ — одной из первых операционных систем, систематически использующей параллелизм. Название THE является аббревиатурой от Technische Hogeschool Eindhoven — университета, в котором Эдсгер Дейкстра работал в то время. Вероятно, самой известной стала его статья “Go-To Statement Considered Harmful”, в которой он убедительно продемонстрировал опасность неструктурированных потоков управления.

Генеалогическое дерево языка Algol выглядит впечатляюще.



Обратите внимание на языки Simula67 и Pascal. Они являются предшественниками многих (вероятно, большинства) современных языков.


Ссылки

Dijkstra, EdsgerW. “Algol 60 Translation: An Algol 60 Translator for the x1 and Making a Translator for Algol 60”. Report MR 35/61. Mathematisch Centrum (Amsterdam), 1961.

Dijkstra, Edsger. “Go-To Statement Considered Harmful”. Communications of the ACM, Vol. 11 No. 3, 1968.

Lindsey, C. H. “The History of Algol-68”. Proceedings of the ACM History of Programming Languages Conference (HOPL-2). ACM SIGPLAN Notices, Vol. 28 No. 3, 1993.

Naur, Peter, ed. “Revised Report on the Algorithmic Language Algol 60”. A/S Regnecentralen (Copenhagen), 1964.

Naur, Peter. “Proof of Algorithms by General Snapshots”. BIT, Vol. 6, 1966, p. 310–316. Вероятно, первая статья о том, как доказать правильность программы.

Naur, Peter. “The European Side of the Last Phase of the Development of ALGOL 60”. ACM SIGPLAN Notices, Vol. 13 No. 8, 1978. Special Issue: History of Programming Languages Conference.

Perlis, Alan J. “The American Side of the Development of Algol”. ACM SIGPLAN Notices, Vol. 13 No. 8, 1978. Special Issue: History of Programming Languages Conference.

van Wijngaarden, A., B. J. Mailloux, J. E. L. Peck, C. H. A. Koster, M. Sintzoff, C. H. Lindsey, L. G. L. T. Meertens, and R. G. Fisker, eds. Revised Report on the Algorithmic Language Algol 68 (Sept. 1973). Springer-Verlag, 1976.

22.2.3.2. Язык программирования Pascal

Язык Algol-68, указанный на генеалогическом дереве семейства языков Algol, был крупным и амбициозным проектом. Подобно языку Algol-60, он был разработан комитетом по языку Algol (рабочей группой IFIP 2.1), но его реализация затянулась до бесконечности, и многие просто потеряли терпение и стали сомневаться, что из этого проекта получится что-нибудь полезное. Один из членов комитета по языку, Никлаус Вирт (Niklaus Wirth), решил разработать и реализовать свой собственный язык, являющийся наследником языка Algol. В противоположность языку Algol-68, его язык, названный Pascal, был упрощенным вариантом языка Algol-60.

Разработка языка Pascal была завершена в 1970 году, и в результате он действительно оказался простым и достаточно гибким. Часто утверждают, что он был предназначен только для преподавания, но в ранних статьях его представляли как альтернативу языка Fortran, предназначенную для тогдашних суперкомпьютеров. Язык Pascal действительно несложно выучить, и после появления легко переносимых реализаций он стал популярным языком, который использовали для преподавания программирования, но языку Fortran он не смог составить конкуренции.



Язык Pascal создан профессором Никлаусом Виртом (Niklaus Wirth) из Технического университета Швейцарии в Цюрихе (Technical University of Switzerland in Zurich — ETH). Выше приведены его фотографии, сделанные в 1969 и 2004 годах. Он получил степень доктора философии (по электротехнике и компьютерным наукам) в Калифорнийском университете в Беркли (University of California at Berkeley) и на протяжении всей своей долгой жизни поддерживал связь с Калифорнией. Профессор Вирт был наиболее полным воплощением идеала профессионального разработчика языков программирования. На протяжении двадцати пяти лет от разработал и реализовал следующие языки программирования.

• Algol W.

• PL/360.

• Euler.

• Pascal.

• Modula.

• Modula-2.

• Oberon.

• Oberon-2.

• Lola (язык описания аппаратного обеспечения).


Никлаус Вирт описывал свою деятельность как бесконечный поиск простоты. Его работа оказала наибольшее влияние на программирование. Изучение этого ряда языков программирования представляет собой чрезвычайно интересное занятие. Профессор Вирт — единственный человек, представивший на конференции HOPL (History of Programming Languages) два языка программирования.

В итоге оказалось, что язык Pascal слишком простой и негибкий, чтобы найти широкое промышленное применение. В 1980-х годах его спасла от забвения работа Андерса Хейльсберга (Anders Hejlsberg) — одного из трех основателей компании Borland. Он первым разработал и реализовал язык Turbo Pascal (который, наряду со многими другими возможностями, воплотил гибкие механизмы передачи аргументов), а позднее добавил в него объектную модель, подобную модели языка С++ (допускающую лишь одиночное наследование и имеющую прекрасный модульный механизм). Он получил образование в Техническом университете Копенгагена (Technical University in Copenhagen), в котором Питер Наур иногда читал лекции, — мир, как известно, тесен. Позднее Андерс Хейльсберг разработал язык Delphi для компании Borland и язык C# для компании Microsoft. Упрощенное генеалогическое дерево семейства языков Pascal показано ниже.



Ссылки

Borland/Turbo Pascal. http://en.wikipedia.org/wiki/Turbo_Pascal.

Hejlsberg, Anders, ScottWiltamuth, and Peter Golde. The C# Programming Language, Second Edition. Microsoft .NET Development Series. ISBN 0321334434.

Wirth, Niklaus. “The Programming Language Pascal”. Acta Informatics, Vol. 1 Fasc 1, 1971.

Wirth, Niklaus. “Design and Implementation of Modula”. Software—Practice and Experience, Vol. 7 No. 1, 1977.

Wirth, Niklaus. “Recollections about the Development of Pascal”. Proceedings of the ACM History of Programming Languages Conference (HOPL-2). ACM SIGPLAN Notices, Vol. 28 No. 3, 1993.

Wirth, Niklaus. Modula-2 and Oberon. Proceedings of the Third ACM SIGPLAN Conference on the History of Programming Languages (HOPL-III). San Diego, CA, 2007. http://portal.acm.org/toc.cfm?id=1238844.

22.2.3.3. Язык программирования Ada

Язык программирования Ada предназначался для решения любых задач программирования, возникающих в Министерстве обороны США. В частности, он должен был стать языком, обеспечивающим создание читабельного и легко сопровождаемого кода для встроенных систем программирования. Его наиболее очевидными предками являются языки Pascal и Simula (см. раздел 22.2.6). Лидером группы разработчиков языка Ada был Жан Ишбиа (Jean Ichbiah), который ранее был председателем группы Simula Users’ Group. При разработке языка Ada основное внимание было уделено

• абстракции данных (но без наследования до 1995 года);

• строгой проверке статических типов;

• непосредственной языковой поддержке параллелизма.


 Целью проекта Ada было воплощение принципов разработки программного обеспечения. В силу этого Министерство обороны не разрабатывало не язык, а сам процесс проектирования языка. В этом процессе принимали участие огромное число людей и организаций, которые конкурировали друг с другом за создание наилучшей спецификации и наилучшего языка, воплощающего идеи победившей спецификации. Этим огромным двадцатилетним проектом (1975–1998 гг.) с 1980 года управлял отдел AJPO (Ada Joint Program Office).

В 1979 году язык получил название в честь леди Аугусты Ады Лавлейс (Augusta Ada Lovelace), дочери поэта лорда Байрона (Byron). Леди Лавлейс можно назвать первой программисткой современности (если немного расширить понятие современности), поскольку она сотрудничала с Чарльзом Бэббиджем (Charles Babbage), лукасианским профессором математики в Кембридже (т.е. занимавшим должность, которую ранее занимал Ньютон!) в процессе создания революционного механического компьютера в 1840-х годах. К сожалению, машина Бэббиджа на практике себя не оправдала.



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

Министерство обороны США много лет предписывало использовать язык Ada в военных приложениях, что в итоге выразилось в афоризме: “Язык Ada — это не просто хорошая идея, это — закон!” Сначала язык Ada просто “настоятельно рекомендовался” к использованию, но, когда многим проектировщикам было прямо запрещено использовать другие языки программирования (как правило, С++), Конгресс США принял закон, требующий, чтобы в большинстве военных приложениях использовался только язык Ada. Под влиянием рыночных и технических реалий этот закон был впоследствии отменен. Таким образом, Бьярне Страуструп был одним и очень немногих людей, чья работа была запрещена Конгрессом США.

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

Язык Ada стал военным стандартом в 1980 году, стандарт ANSI был принят в 1983 году (первая реализация появилась в 1983 году — через три года после издания первого стандарта!), а стандарт ISO — в 1987 году. Стандарт ISO был сильно пересмотрен (конечно, сравнительно) в издании 1995 года. Включение в стандарт значительных улучшений повысило гибкость механизмов параллелизма и поддержки наследования.


Ссылки

Barnes, John. Programming in Ada 2005. Addison-Wesley, 2006. ISBN 0321340787.

Consolidated Ada Reference Manual, consisting of the international standard (ISO/IEC 8652:1995). Information Technology — Programming Languages — Ada, as updated by changes from Technical Corrigendum 1 (ISO/IEC 8652:1995:TC1:2000). Официальная домашняя страница языка Ada: www.usdoj.gov/crt/ada/.

Whitaker, William A. ADA — The Project: The DoD High Order Language Working Group. Proceedings of the ACM History of Programming Languages Conference (HOPL-2). ACM SIGPLAN Notices, Vol. 28 No. 3, 1993.

22.2.4. Язык программирования Simula

Язык Simula был разработан в первой половине 1960-х годов Кристеном Нюгордом (Kristen Nygaard) и Оле-Йоханом Далем (Ole-Johan Dahl) в Норвежском вычислительном центре (Norwegian Computing Center) и университете Осло (Oslo University). Язык Simula несомненно принадлежит семейству языков Algol. Фактически язык Simula является практически полным надмножеством языка Algol-60. Однако мы уделили особое внимание языку Simula, потому что он является источником большинства фундаментальных идей, которые сегодня называют объектно-ориентированным программированием. Он был первым языком, в котором реализованы наследование и виртуальные функции. Слова class для пользовательского типа и virtual для функции, которую можно заместить и вызвать с помощью интерфейса базового класса, пришли в С++ из языка Simula.

 Вклад языка Simula не ограничен языковыми свойствами. Он состоит в явно выраженном понятии объектно-ориентированного проектирования, основанного на идее моделирования реальных явлений в коде программы.

• Представление идей в виде классов и объектов классов.

• Представление иерархических отношений в виде иерархии классов (наследование).


Таким образом, программа становится множеством взаимодействующих объектов, а не монолитом.



Кристен Нюгорд — один из создателей языка Simula 67 (вместе с Оле-Йоханом Далем, на фото слева в очках) — был энергичным и щедрым гигантом (в том числе по росту). Он посеял семена фундаментальных идей объектно-ориентированного программирования и проектирования, особенно наследования, и неотступно придерживался их на протяжении десятилетий. Его никогда не устраивали простые, краткие и близорукие ответы. Социальные вопросы также волновали его на протяжении десятков лет. Он искренне выступал против вступления Норвегии в Европейский Союз, видя в этом опасность излишней централизации, бюрократизации и пренебрежения интересами маленькой страны, находящейся на далеком краю Союза. В середине 1970-х годов Кристен Нюгорд отдавал значительное время работе на факультете компьютерных наук в университете Аархуса (University of Aarhus) в Дании, где в это время Бьярне Страуструп проходил обучение по программе магистров.

Магистерскую степень по математике Кристен Нюгорд получил в университете Осло (University of Oslo). Он умер в 2002 году, всего через месяц после того, как (вместе с другом всей своей жизни Оле-Йоханом Далем) получил премию Тьюринга — наивысший знак почета, которым Ассоциация по вычислительной технике (Association for Computing Machiner — ACM) отмечает выдающихся ученых в области компьютерных наук.

Оле-Йохан Дал был более традиционным академическим ученым. Его очень интересовали спецификации языков и формальные методы. В 1968 году он стал первым профессором по информатике (компьютерным наукам) в университете Осло.



В августе 2000 года король Норвегии объявил Даля и Нюгорда командорами ордена Святого Олафа (Commanders of the Order of Saint Olav). Все таки есть пророки в своем отечестве!


Ссылки

Birtwistle, G., O-J. Dahl, B. Myhrhaug, and K. Nygaard: SIMULA Begin. Studentlitteratur (Lund. Sweden), 1979. ISBN 9144062125.

Holmevik, J. R. “Compiling SIMULA: A Historical Study of Technological Genesis”. IEEE Annals of the History of Computing, Vol. 16 No. 4, 1994, p. 25–37.

Kristen Nygaard’s homepage: http://heim.ifi.uio.no/~kristen/.

Krogdahl, S. “The Birth of Simula”. Proceedings of the HiNC 1 Conference in Trondheim, June 2003 (IFIP WG 9.7, in cooperation with IFIP TC 3).

Nygaard, Kristen, and Ole-Johan Dahl. “The Development of the SIMULA Languages”. ACM SIGPLAN Notices, Vol. 13 No. 8, 1978. Special Issue: History of Programming Languages Conference.

SIMULA Standard. DATA processing — Programming languages — SIMULA. Swedish Standard, Stockholm, Sweden (1987). ISBN 9171622349.

22.2.5. Язык программирования С

 В 1970-м году считалось, что серьезное системное программирование — в частности, реализация операционной системы — должно выполняться в ассемблерном коде и не может быть переносимым. Это очень напоминало ситуацию, сложившуюся в научном программировании перед появлением языка Fortran. Несколько индивидуумов и групп бросили вызов этой ортодоксальной точке зрения. В долгосрочной перспективе язык программирования C оказался наилучшим результатом этих усилий (подробнее об этом — в главе 27).

Деннис Ритчи (Dennis Ritchie) разработал и реализовал язык программирования С в Исследовательском центре по компьютерным наукам (Computer Science Research Center) компании Bell Telephone Laboratories в Мюррей-Хилл, штат НьюДжерси (Murray Hill, New Jersey). Прелесть языка С в том, что он был преднамеренно простым языком программирования, позволявшим непосредственно учитывать фундаментальные аспекты аппаратного обеспечения. Большинство усложнений (которые в основном были позаимствованы у языка С++ для обеспечения совместимости) было внесено позднее и часто вопреки желанию Денниса Ритчи. Частично успех языка С объясняется его широкой доступностью, но его реальная сила проявлялась в непосредственном отображении свойств языка на свойства аппаратного обеспечения (см. разделы 25.4–25.5). Деннис Ритчи лаконично описывал язык С как строго типизированный язык со слабым механизмом проверки; иначе говоря, язык С имел систему статических (распознаваемых на этапе компиляции) типов, а программа, использовавшая объект вопреки его определению, считалась неверной. Однако компилятор языка С не мог распознавать такие ситуации. Это было логично, поскольку компилятор языка С мог выполняться в памяти, размер которой составлял 48К. Вскоре язык С вошел в практику, и люди написали программу lint, которая отдельно от компилятора проверяла соответствие кода системе типов.



Кен Томпсон (Ken Thompson) и Деннис Ритчи стали авторами системы Unix, возможно, наиболее важной операционной системы за все времена. Язык C ассоциировался и по-прежнему ассоциируется с операционной системой Unix, а через нее — с системой Linux и движением за открытый код.

Деннис Ритчи вышел на пенсию из компании Lucent Bell Labs. На протяжении сорока лет он работал в Исследовательском центре по компьютерным наукам компании Bell Telephone. Он закончил Гарвардский университет (Harvard University) по специальности “физика”, степень доктора философии в прикладной математике он также получил в этом университете.



В 1974–1979 годах на развитие и адаптацию языка С++ оказали влияние многие люди из компании Bell Labs. В частности, Дуг Мак-Илрой (Doug McIlroy) был всеобщим любимцем, критиком, собеседником и генератором идей. Он оказал влияние не только на языки C и C++, но и на операционную систему Unix, а также на многое другое.

Брайан Керниган (Brian Kernighan) — программист и экстраординарный писатель. Его программы и проза — образцы ясности. Стиль этой книги частично объясняется подражанием его шедевру — учебнику The C Programming Language (известным как K&R по первым буквам фамилий его авторов — Брайана Кернигана и Денниса Ритчи).

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



В течение многих лет Брайан Керниган участвовал во многих важных программистских и издательских проектах. В качестве примера можно назвать язык AWK — один из первых языков подготовки сценариев, получивший название по инициалам своих авторов (Aho, Weinberger и Kernighan), а также AMPL — (A Mathematical Programming Language — язык для математического программирования).

В настоящее время Брайан Керниган — профессор Принстонского университета (Princeton University); он превосходный преподаватель, ясно излагающий сложные темы. Более тридцати лет он работал в Исследовательском центре по компьютерным наукам компании Bell Telephone. Позднее компания Bell Labs стала называться AT&T Bell Labs, а потом разделилась на компании AT&T Labs и Lucent Bell Labs. Брайан Керниган закончил университет Торонто (University of Toronto) по специальности физика; степень доктора философии по электротехнике он получил в Принстонском университете.

Генеалогическое дерево семейства языка C представлено ниже.



Корни языка С уходят в так никогда и не завершившийся проект по разработке языка CPL в Англии, язык BCPL (Basic CPL), разработанный сотрудником Кембриджского университета (Cambridge University) Мартином Ричардсом (Martin Richards) во время его посещения Массачусетсского технологического института (MIT), а также в интерпретируемый язык B, созданный Кеном Томпсоном. Позднее язык C был стандартизован институтами ANSI и ISO и подвергся сильному влиянию языка C++ (например, в нем появились проверка аргументов функций и ключевое слово const).

Разработка языка CPL была целью совместного проекта Кембриджского университета и Имперского колледжа Лондона (Imperial College). Изначально планировалось выполнить проект в Кембридже, поэтому буква “C” официально означает слово “Cambridge”. Когда партнером в проекте стал Имперский колледж, официальным объяснением буквы “C” стало слово “Combined” (“совместный”). На самом деле (по крайней мере, нам рассказывали) его всегда связывали с именем Christopher в честь Кристофера Стрэчи (Christopher Strachey), основного разработчика языка CPL.


Ссылки

Домашняя веб-страница Брайана Кернигана: http://cm.bell-labs.com/cm/cs/ who/bwk.

Домашняя веб-страница Денниса Ритчи: http://cm.bell-labs.com/cm/cs/who/dmr. ISO/IEIC 9899:1999. Programming Languages — C. (The C standard.)

Kernighan, Brian, and Dennis Ritchie. The C Programming Language. Prentice Hall, 1978. Second Edition, 1989. ISBN 0131103628.

Список сотрудников Исследовательского центра по компьютерным наукам компании Bell Labs: http://cm.bell-labs.com/cm/cs/alumni.html.

Ritchards, Martin. BCPL — The Language and Its Compiler. Cambridge University Press, 1980. ISBN 0521219655.

Ritchie, Dennis. The Development of the C Programming Language. Proceedings of the ACM History of Programming Languages Conference (HOPL-2). ACM SIGPLAN Notices, Vol. 28 No. 3, 1993.

Salus, Peter. A Quarter Century of UNIX. Addison-Wesley, 1994. ISBN 0201547775.

22.2.6. Язык программирования С++

 Язык C++ — универсальный язык программирования с уклоном в системное программирование. Перечислим его основные свойства.

• Он лучше языка С.

• Поддерживает абстракцию данных.

• Поддерживает объектно-ориентированное программирование.

• Поддерживает обобщенное программирование.


Язык С++ был разработан и реализован Бьярне Страуструпом из Исследовательского центра по компьютерным наукам компании Bell Telephone Laboratories в Мюррей-Хилл (Murray Hill), штат Нью-Джерси (New Jersey), где работали также Деннис Ритчи, Брайан Керниган, Кен Томпсон, Дуг Мак-Илрой и другие великаны системы Unix.



 Бьярне Страуструп получил степень магистра по математике и компьютерным наукам в своем родном городе Эрхусе (Еrhus), Дания. Затем он переехал в Кембридж (Cambridge), где получил степень доктора философии по компьютерным наукам, работая с Дэвидом Уилером (David Wheeler). Цель создания языка С+ заключалась в следующем.

• Сделать методы абстрагирования доступными и управляемыми в рамках широко распространенных проектов.

• Внедрить объектно-ориентированное и обобщенное программирование в прикладные области, где основным критерием успеха является эффективность.


До появления языка С++ эти методы (часто необоснованно объединяемые под общим названием “объектно-ориентированное программирование”) были практически неизвестны в индустрии. Как и в научном программировании до появления языка Fortran, так и в системном программировании до появления языка С считалось, что эти технологии слишком дорогие для использования в реальных приложениях и слишком сложные для обычных программистов.

Работа над языком С++ началась в 1979 году, а в 1985 году он был выпущен для коммерческого использования. Затем Бьярне Страуструп и его друзья из компании Bell Labs и нескольких других организаций продолжали совершенствовать язык С++, и в 1990 году началась официальная процедура его стандартизации. С тех пор определение языка C++ было сначала разработано ANSI (Национальный институт стандартизации США), а с 1991 года — ISO (Международная организация по стандартизации). Бьярне Страуструп играл главную роль в этом процессе, занимая должность председателя ключевой подгруппы, ответственной за создание новых свойств языка. Первый международный стандарт (C++98) был ратифицирован в 1998 году, а над вторым стандартом (C++0x) работа продолжается по сей день.

Наиболее значительным событием в истории языка С++ спустя десять лет после его появления стала стандартная библиотека контейнеров и алгоритмов — STL. Она стала результатом многолетней работы, в основном под руководством Александра Степанова (Alexander Stepanov), направленной на создание как можно более универсального и эффективного программного обеспечения и вдохновляемой красотой и полезностью математики.



Алекс Степанов — изобретатель библиотеки STL и пионер обобщенного программирования. Он закончил

Московский государственный университет и работал в области робототехники и алгоритмов, используя разные языки программирования (включая Ada, Scheme и C++). С 1979 года он работал в академических организациях США, а также в промышленных компаниях, таких как GE Labs, AT&T Bell Labs, Hewlett-Packard, Silicon Graphics и Adobe.

Генеалогическое дерево языка C++ приведено ниже.

Язык C with Classes был создан Бьярне Страуструпом как результат синтеза идей языков C и Simula. Этот язык вышел из употребления сразу после реализации его наследника — языка C++.



 Обсуждение языков программирования часто сосредоточено на их элегантности и новых свойствах. Однако языки С и С++ стали самыми успешными языками программирования за всю историю компьютерных технологий не поэтому: их сила заключается в гибкости, производительности и устойчивости. Большинство систем программного обеспечения существует несколько десятилетий, часто исчерпывая свои аппаратные ресурсы и подвергаясь совершенно неожиданным изменениям. Языки С и С++ смогли преуспеть в этой среде. Мы очень любим изречение Денниса Ритчи: “Одни языки люди разрабатывали, чтобы доказать свою правоту, а другие — для того, чтобы решить задачу”. Язык С относится ко второй категории языков. Бьярне Страуструп любит говорить: “Даже я знаю, как разработать язык, который красивее языка С++”. Цель языка С++, как и языка С, — не абстрактная красота (хотя мы очень ее ценим), а полезность.

Я часто сожалел, что не мог использовать в этой книге возможности версии C++0x. Это упростило бы многие примеры и объяснения. Примерами компонентов стандартной библиотеки версии С++0х являются классы

unordered_map
(см. раздел 21.6.4),
array
(см. раздел 20.9) и
regexp
(см. разделы 23.5–23.9). В версии C++0x будет более тщательная проверка шаблонов, более простая и универсальная инициализация, а также более ясная система обозначений (см. мое выступление на конференции HOPL-III).


Ссылки

Публикации Александра Степанова: www.stepanovpapers.com.

Домашняя страница Бьярне Страуструпа: www.research.att.com/~bs.

ISO/IEC 14882:2003. Programming Languages — C++. (Стандарт языка C++.)

Stroustrup, Bjarne. “A History of C++: 1979–1991. Proceedings of the ACM History of Programming Languages Conference (HOPL-2). ACM SIGPLAN Notices, Vol. 28 No. 3, 1993.

Stroustrup, Bjarne. The Design and Evolution of C++. Addison-Wesley, 1994. ISBN 0201543303.

Stroustrup, Bjarne. The C++ Programming Language (Special Edition). Addison-Wesley, 2000. ISBN 0201700735.

Stroustrup, Bjarne. “C and C++: Siblings”; “C and C++: A Case for Compatibility”; and “C and C++: Case Studies in Compatibility”. The C/C++ Users Journal. July, Aug., and Sept. 2002.

Stroustrup, Bjarne. “Evolving a Language in and for the RealWorld: C++ 1991–2006”. Proceedings of the Third ACM SIGPLAN Conference on the History of Programming Languages (HOPL-III). San Diego, CA, 2007. http://portal.acm.org/toc. cfm?id=1238844.

22.2.7. Современное состояние дел

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



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

Тем не менее мы обязаны ответить на этот вопрос, поскольку в 2008 году в мире было около десяти миллионов профессиональных программистов. Об этом свидетельствуют отчет С89 С++ компании IDC (специализирующейся на сборе данных), дискуссии с издателями и поставщиками компиляторов, а также различные источники в сети веб. Можете с нами спорить, но нам точно известно, что от одного до ста миллионов человек хотя бы наполовину подходят под разумное определение программиста. Какие языки они используют? Вероятно (просто вероятно), что более 90% их программ написано на языках Ada, C, C++, C#, COBOL, Fortran, Java, PERL, PHP и Visual Basic.

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

22.2.8. Источники информации

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


Страницы и фотографии разработчиков языков программирования

www.angelfire.com/tx4/cus/people/.


Несколько примеров языков программирования

http://dmoz.org/Computers/Programming/Languages/.


Учебники

Scott, Michael L. Programming Language Pragmatics. Morgan Kaufmann, 2000. ISBN 1558604421.

Sebesta, Robert W. Concepts of Programming Languages. Addison-Wesley, 2003. ISBN 0321193628.


Книги об истории языков программирования

Bergin, T.J., and R.G. Gibson, eds. History of Programming Languages — II. Addison-Wesley, 1996. ISBN 0202295021.

Hailpern, Brent, and Barbara G. Ryder, eds. Proceedings of the Third ACM SIGPLAN Conference on the History of Programming Languages (HOPL-III).

San Diego, CA, 2007. http://portal.acm.org/toc.cfm?id=1238844.

Lohr, Steve. Go To: The Story of the Math Majors, Bridge Players, Engineers, Chess Wizards, Maverick Scientists and Iconoclasts—The Programmers Who Created the Software Revolution. Basic Books, 2002. ISBN 9780465042265.

Sammet, Jean. Programming Languages: History and Fundamentals. Prentice-Hall, 1969. ISBN 0137299885.

Wexelblat, Richard L., ed. History of Programming Languages. Academic Press, 1981. ISBN 0127450408.


Контрольные вопросы

1. Зачем нужна история?

2. Зачем нужны языки программирования? Приведите примеры.

3. Перечислите некоторые фундаментальные принципы хороших языков программирования.

4. Что такое абстракция? Что такое высокий уровень абстракции?

5. Назовите высокоуровневые идеалы программирования.

6. Перечислите потенциальные преимущества высокоуровневого программирования.

7. Что такое повторное использование кода и в чем заключается его польза?

8. Что такое процедурное программирование? Приведите конкретный пример.

9. Что такое абстракция данных? Приведите конкретный пример.

10. Что такое объектно-ориентированное программирование? Приведите конкретный пример.

11. Что такое обобщенное программирование? Приведите конкретный пример.

12. Что такое мультипарадигменное программирование? Приведите конкретный пример.

13. Когда была выполнена первая программа на компьютере, допускающем хранение данных в памяти?

14. Какую выдающуюся работу выполнил Дэвид Уилер?

15. Расскажите об основном вкладе Джона Бэкуса в создание первого языка программирования.

16. Какой первый язык разработала Грейс Мюррей Хоппер?

17. В какой области компьютерных наук выполнил свою главную работу Джон Мак-Карти?

18. Какой вклад внес Питер Наур в создание языка Algol-60?

19. Какую выдающуюся работу выполнил Эдсгер Дейкстра?

20. Какой язык спроектировал и реализовал Никлаус Вирт?

21. Какой язык разработал Андерс Хейльсберг?

22. Какова роль Жана Ишбиа в проекте Ada?

23. Какой стиль программирования впервые открыл язык Simula?

24. Где (кроме Осло) преподавал Кристен Нюгорд?

25. Какую выдающуюся работу выполнил Оле-Йохан Дал?

26. Какая операционная система была разработана под руководством Кена Томпсона?

27. Какую выдающуюся работу выполнил Дуг Мак-Илрой?

28. Назовите наиболее известную книгу Брайана Кернигана.

29. Где работал Деннис Ритчи?

30. Какую выдающуюся работу выполнил Бьярне Страуструп?

31. Какие языки пытался использовать Алекс Степанов для проектирования библиотеки STL?

32. Назовите десять языков программирования, не описанных в разделе 22.2.

33. Диалектом какого языка программирования является язык Scheme?

34. Назовите два наиболее известных наследника языка C++.

35. Почему язык C стал частью языка C++?

36. Является ли слово Fortran аббревиатурой? Если да, то какие слова в нем использованы?

37. Является ли слово COBOL аббревиатурой? Если да, то какие слова в нем использованы?

38. Является ли слово Lisp аббревиатурой? Если да, то какие слова в нем использованы?

39. Является ли слово Pascal аббревиатурой? Если да, то какие слова в нем использованы?

40. Является ли слово Ada аббревиатурой? Если да, то какие слова в нем использованы?

41. Назовите самый лучший язык программирования.


Термины

В этой главе раздел “Термины” содержит названия языков, имена людей и названия организаций.

• Языки

• Ada

• Algol

• BCPL

• C

• C++

• COBOL

• Fortran

• Lisp

• Pascal

• Scheme

• Simula

• Люди

• Чарльз Бэббидж

• Джон Бэкус

• Оле-Йохан Дал

• Эдсгер Дейкстра

• Андерс Хейльсберг

• Грейс Мюррей Хоппер

• Жан Ишбиа

• Брайан Керниган

• Джон Маккарти

• Дуг Мак-Илрой

• Питер Наур

• Кристен Нюгорд

• Деннис Ритчи

• Алекс Степанов

• Бьярне Страуструп

• Кен Томпсон

• Дэвид Уилер

• Никлаус Вирт

• Организации

• Bell Laboratories

• Borland

• Cambridge University (England)

• ETH (Швейцарский федеральный технический университет)

• IBM

• MIT

• Norwegian Computer Center

• Princeton University

• Stanford University

• Technical University of Copenhagen

• U.S. Department of Defense

• U.S. Navy


Упражнения

1. Дайте определение понятия программирование.

2. Дайте определение понятия язык программирования.

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

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

5. Напишите программу “Hello, World!” на каждом из языков, упомянутых в этой главе.

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

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

8. Зачем нужен язык С++? Напишите 10–20-страничное сочинение.

9. Зачем нужен язык С? Напишите 10–20-страничное сочинение.

10. Выберите один язык программирования (не C и не C++) и напишите 10–20-страничное сочинение о его истории, целях и возможностях. Приведите много конкретных примеров. Кто использует эти языки и почему?

11. Кто в настоящее время занимает Лукасианскую кафедру в Кембридже (Lucasian Chair in Cambridge)?

12. Кто из разработчиков языков программирования, перечисленных в главе, имеет научную степень по математике, а кто нет?

13. Кто из разработчиков языков программирования, перечисленных в главе, имеет степень доктора философии, а кто нет? В какой области?

14. Кто из разработчиков языков программирования, перечисленных в главе, является лауреатом премии Тьюринга? За какие достижения? Найдите официальные объявления о присуждении премии Тьюринга лауреатам, упомянутым в главе.

15. Напишите программу, которая считывает файл, содержащий пары (имя, год), например (Algol,1960) и (C,1974), и рисует соответствующий график.

16. Модифицируйте программу из предыдущего упражнения так, чтобы она считывала из файла кортежи (имя, год, (предшественники)), например (Fortran, 1956, ()), (Algol, 1960, (Fortran)) и (C++, 1985, (C, Simula)), и рисовала граф со стрелками, направленными от предшественников к последователям. Используя эту программу, нарисуйте улучшенные варианты диаграмм из разделов 22.2.2 и 22.2.7.


Послесловие

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

Глава 23