К читателям версии, опубликованной на сайте клуба Весельчак У.Имея в своем распоряжении возможность публикации статей на нашем сайте, я решил воспользоваться ей и представить на обозрение участников клуба первую часть проекта статьи, которая, возможно, впоследствии будет опубликована в новом журнале.
Надеюсь, что ваши отзывы помогут мне сделать ее лучше и интереснее.
ПредисловиеВ данной статье я попытаюсь изложить свой взгляд на историю развития языков программирования, дав при этом по возможности каждому из них краткую характеристику. Разумеется, эта характеристика не претендует ни на полноту изложения, ни на истинность в последней инстанции. Не могу также гарантировать непредвзятость и объективность (хотя очень попытаюсь ее сохранять), поскольку некоторые из упомянутых языков мне в силу разных причин интереснее, чем другие.
Изложение материала также не будет однородным, поскольку с некоторыми из языков я работал продолжительное время и успел набрать необходимый опыт, с другими работал лишь эпизодически, с третьими вообще знаком лишь понаслышке и вынужден составить свое мнение о них с чужих слов.
Поэтому буду благодарен читателям за любые конструктивные замечания, дополнения и исправления, которые смогут улучшить качество данной статьи.
Как все начиналосьНа первый взгляд это может показаться странным, но программирование старше, чем языки программирования.
Первые программисты обходились вовсе без языков программирования. Самые первые цифровые электронно-вычислительные машины создавались для конкретных, узкоспециализированных задач (например, для решения систем линейных уравнений методом Гаусса), и программу их работы изменить было невозможно в принципе. Естественно, вопрос программирования подобных вычислительных систем отпадал сам собой можно было только менять входные данные.
Последовавшие за ними вычислительные машины с изменяемой программой также были лишены возможности использования языков программирования: программы вводились в них посредством соединения гнезд на специальной коммутационной панели, которая управляла последовательностью выполняемых операций. Разумеется, автоматизировать этот трудоемкий и сложный процесс не представлялось возможным.
Хотя к этому времени (40-е годы XX столетия) уже была известна модель хранимой программы фон Неймана, элементная база еще не позволяла создавать оперативные запоминающие устройства достаточного объема для хранения кодов инструкций, их едва хватало для хранения данных (память размером в 1000 машинных слов считалась огромной и стоила астрономическую сумму). Поэтому между теорией и практическим ее воплощением прошло несколько лет, пока, наконец, не появились действительно универсальные цифровые ЭВМ, позволяющие быстро менять программы путем загрузки их в оперативную память. Громоздкие коммутационные панели, опутанные клубками проводов, исчезли, их заменили массивы кодов инструкций, загружаемых с различных устройств хранения информации: перфолент и перфокарт, магнитных лент, барабанов
С появлением таких устройств и зародилось явление, которое довольно быстро из досадной проблемы переросло в настоящий кризис, кризис программирования. Выяснилось, что разрабатывать программы в машинных кодах слишком трудно и долго, а следовательно, дорого. На фоне непрерывного снижения цен на оборудование увеличение затрат на программное обеспечение раздражало владельцев компьютеров (если бы они только знали, что ждет их в будущем!). Поиск решения этой проблемы и привел к появлению ныне столь многочисленной семьи языков программирования.
АссемблерПоскольку большинство промышленных компьютеров обладало фон-неймановской архитектурой, их программы представляли собой не что иное, как последовательности машинных слов определенной структуры. А если так, то ничто не мешает разработать программу, которая будет выдавать такие последовательности в качестве своих выходных данных.
Разумеется, все выглядит так просто лишь на первый взгляд. Для того, чтобы сделать эту идею реальностью, потребовалось много лет упорнейшего труда математиков, лингвистов и инженеров, которые разработали теорию формальных языков и превратили разработку компиляторов из занятия, доступного лишь избранным, в хорошо формализованный процесс, которому обучают в большинстве учебных заведений компьютерного профиля.
Самым первым и очевидным шагом на пути автоматизации программирования было освободить программистов от самой рутинной части их работы. Самыми рутинными операциями при программировании в кодах были:
- формирование кода команды;
- вычисление физических адресов ячеек с данными;
- вычисление смещений для команд условного и безусловного ветвлений.
Для первой проблемы было найдено следующее решение: для каждой машинной команды были подобраны краткие мнемоники, отражающие ее суть. Например, для команды пересылки данных -
MOV, для команды сложения -
ADD и т.п. Такие мнемоники намного легче запомнить, чем двоичные, восьмеричные или шестнадцатеричные коды команды; кроме того, их не в пример легче читать, а это тоже немаловажно при отладке программы или ее изменении.
Вторая и третья проблемы также были решены без особых усилий: вместо использования реальных адресов в тексте программы располагались символические метки, а программа, генерирующая машинный код, вычисляла фактическое расположение ячеек в оперативной памяти и поставляла его в код.
Программы, предназначенные для преобразования подобных мнемонических инструкций в исполняемый машинный код, стали называть
ассемблерами, а языки, используемые для записи таких программ, - языками ассемблера, или, кратко, тоже ассемблерами.
Разумеется, появление ассемблеров явилось важным шагом на пути развития программирования. Во-первых, программы на ассемблере читать и понимать гораздо легче, чем разбираться с машинными кодами (однако не следует считать, что это легко - все познается в сравнении). Во-вторых, вычисление смещений для ветвлений и относительной адресации - задача хоть и не сложная, но весьма громоздкая и подверженная ошибкам. В-третьих, при добавлении дополнительных команд в середину программы не нужно заботиться о пересчете изменившихся адресов данных и команд, ассемблер их пересчитает при очередном запуске.
Однако применение ассемблера оставляет нерешенными ряд задач. Во-первых, программирование на языке ассемблера требует хорошего знания архитектуры машины, на которой программа будет выполняться. Это значительно сужает круг потенциальных разработчиков программного обеспечения. Например, для разработки программного обеспечения для решения экономических задач программист должен одинаково хорошо разбираться как в предметной области, так и в архитектуре компьютера; наладить совместную работу двух разных людей, каждый из которых является специалистом в одном из этих вопросов, значительно сложнее.
Во-вторых, как следствие, низкий уровень языка вынуждает программиста мыслить не категориями предметной области, а в терминах ячеек памяти, индексных и базовых регистров, указателя стека и т.п. В случае нетривиальной задачи довольно трудно мыслить одновременно на нескольких уровнях абстракции.
В-третьих, язык ассемблера вследствие жесткой привязки к архитектуре определенного компьютера делает программы непереносимыми: переход на другой тип компьютеров делает непригодными все ранее разработанные программы, поскольку новый компьютер имеет другие мнемоники и формат команд, другие методы адресации аргументов и т.п. Кроме того, переучивание программистов на новую систему команд также потребует определенного времени, что делает переход на другую архитектуру еще более трудным.
В качестве примера приведу небольшой отрывок ассемблерного кода, заимствованный из моей статьи "Заметки о рекурсии-2. Механизм рекурсии":
push esi
mov esi,dword ptr [esp+8]
pop eax
cmp esi,eax
jge Fact+10h
xor eax,eax
pop esi
Этот фрагмент предназначен для выполнения на процессоре архитектуры Intel 80x86 или совместимом.
FORTRANFORTRAN был первым успешно реализованным языком программирования высокого уровня. Разрабатывался он во времена жесточайшего дефицита оперативной памяти, который ставил под сомнение саму возможность практической реализации компилятора какого-либо из языков высокого уровня (ЯВУ). Это обстоятельство отложило свой отпечаток на облик языка: во главу угла ставилась высочайшая эффективность и компактность результирующего машинного кода, которые позволили языку постепенно завоевать популярность, несмотря на многочисленные нападки сторонников ассемблера.
Несмотря на отсутствие опыта (откуда же ему было взяться у разработчиков первого компилятора, когда даже теория компиляции еще не была разработана), эта задача была решена разработчиками блестяще: компилятор FORTRAN I для мэйнфреймов IBM удерживал рекорд по степени оптимизации кода в течение 20 лет! Хотя этому меньше удивляешься, узнав, что во главе разработки стоял гениальный Джон Бэкус, а в его команду входили Ш. Бест, Х. Геррик, П. Шеридан, Р. Натт, Р. Нельсон, И. Зиллер, Р. Голдберг, Л. Хейбт и Д. Сейр. Немалую роль сыграла и поддержка коллектива руководством IBM, даже несмотря на то, что проект потребовал куда больше ресурсов, чем первоначально планировалось.
Выиграв кровопролитнейшую битву с ассемблером, новый язык получил заслуженное признание как в среде ученых, так и у промышленников и военных. В частности, трудно переоценить его роль в компьютерном обеспечении космической программы.
Разумеется, первая реализация была небезупречна, поэтому в дальнейшем как язык, так и его компиляторы претерпели долгое эволюционное развитие (которое, как я с удивлением обнаружил, продолжается и поныне).
В 1958 г. увидел свет FORTRAN II, в который были добавлены средства раздельной компиляции модулей, а также возможность связи с ассемблерным кодом. В том же году появился FORTRAN III, который, впрочем, так и не был выпущен для общего пользования. В этой версии можно было включать фрагменты ассемблерного кода прямо в исходный текст программы. Эффективность кода возросла еще больше, однако это сильно ударило по переносимости, поэтому от новшества решили все же отказаться.
Наконец, в 1961 г. был выпущен FORTRAN IV, который представлял собой рафинированный вариант FORTRAN II, лишенный некоторых аномалий и машинно-зависимых особенностей. Именно эта версия получила наибольшее распространение среди ученых и инженеров и служила им верой и правдой более 30 лет, будучи реализованной практически на всех мэйнфреймах, мини-ЭВМ и персональных компьютерах, с которыми мне когда-либо доводилось иметь дело.
С мая 1962 г. началась работа по стандартизации языка, результатом которой стал FORTRAN 66 - первый в мире стандарт на ЯВУ. Язык заслуженно получил очередной титул "первый".
Основные черты языка: программа состоит из главной программы и набора подпрограмм, компилируемых независимо и компонуемых перед загрузкой. Каждая подпрограмма имеет собственную отдельную область памяти для хранения переменных и кода. Кроме того, можно выделять общие (
COMMON) области памяти, которые содержат переменные, доступные из разных подпрограмм. Обмен данными осуществляется через блоки COMMON, а также через формальные параметры подпрограмм, при этом передача параметров производится только по ссылке.
Из типов данных имеются числовые (целочисленные и с плавающей точкой), а также логический. Из структур скалярные переменные и статические массивы размерностью не более 3. Что характерно, имеется также встроенный тип для представления комплексных значений, для которого определен полный набор операций.
Поскольку основное назначение языка - математические вычисления, имеется богатый набор арифметических операций, математических функций, а также преобразований типов.
Управляющие структуры весьма примитивны и представлены в основном условными и безусловными переходами по меткам.
Правила записи конструкций языка весьма строги: строка имеет не более 80 символов, из которых в первой позиции строки предусмотрен символ комментария (обычно C), позиции 2-6 отведены для меток, 7 - для символа продолжения строки (если предыдущая не уместилась в отведенные 80 символов), остальные служат для записи операторов.
Хранение локальных переменных в специально выделенной области памяти весьма затрудняет использование рекурсии.
Небольшая программа на языке FORTRAN IV:
DIMENSION A(99)
10 READ(1,5) K
5 FORMAT (I2)
IF (K.EQ.0) GO TO 30
READ (1,15) (A(I), I=1,K)
15 FORMAT (6F10.5)
RES=SUM(A,K)
WRITE (2,25) RES
25 FORMAT (5HSUM = ,3X, F10.5)
GOTO 10
30 CONTINUE
END
FORTRAN 77Несмотря на очевидный успех FORTRAN, по мере роста размера и сложности программ все очевиднее становилась его неуклюжесть благодаря примитивным управляющим структурам. Когда умами программистов овладели идеи структурного программирования и использование оператора GO TO стало признаком дурного тона, надолго застывший в своем развитии FORTRAN начал понемногу сдавать свои позиции. С другой стороны, огромные наработки в академической и промышленной среде нельзя было игнорировать. Поэтому спустя 11 лет старый добрый FORTRAN был основательно пересмотрен и переработан с у четом современных (на то время) требований к языку программированию. Плодом этих усилий стал стандарт FORTRAN 77.
Прежде всего были учтены интересы давних сторонников языка: новый язык был в высокой степени совместим с предшественником, т.е. фактически любая корректная программа FORTRAN IV является корректной программой FORTRAN 77. Наряду с этим было добавлено немало новшеств, по большей части в области управляющих структур. Так, появилась долгожданная конструкция
IF ... THEN ... ELSE ... ENDIF. Улучшены были также циклы, например, появился цикл с предусловием (ранее цикл выполнялся минимум один раз, и зачастую приходилось добавлять условный оператор, чтобы обойти цикл при необходимости).
Изменения коснулись и типов данных. Так, добавился символьный тип (ранее литеры хранились в целочисленных переменных, и средства работы с литерами были весьма рудиментарны). Помимо однородных массивов, появились структуры, подобные структурам C и записям Pascal.
На этом эволюция языка-долгожителя не остановилась, и в 1990 г. увидел свет очередной стандарт, FORTRAN 90. Впрочем, он слишком опоздал со своим появлением, дав приличную фору по времени своим конкурентам, главным из которых явился, безусловно, C++. Несмотря на очевидные усовершенствования языка, FORTRAN 90 не получил такого ошеломляющего успеха, как его знаменитые предшественники, и удовольствовался весьма скромной нишей. Не прибавило ему популярности и появление следующего стандарта, FORTRAN 95. Новое поколение программистов ориентировано на C++, Java и C#, и старому доброму FORTRANу, увы, нечего им противопоставить.
COBOLКак явственно следует из самого названия (Common Business Oriented Language), этот язык в первую очередь ориентирован на сферу приложений для экономики и бизнеса. Первая его версия появилась в 1960 г., а последний вариант пересмотренного и дополненного стандарта - в 1972 г.
Специфика предметной области определила основные черты языка: для экономических расчетов характерны несложные алгоритмы при большом объеме ввода-вывода. Также для облегчения обучения целевой аудитории языку его синтаксис был максимально приближен к разговорному английскому. В результате практически каждый владеющий английским в состоянии прочитать и понять программу на COBOLе без дополнительных комментариев, даже если раньше ему не доводилось иметь дело с этим языком: программа больше напоминает рассказ о выполняемой последовательности действий, чем собственно программу.
Учитывая многообразие оборудования, сложившееся ко времени появления языка, при его разработке были предприняты меры, повышающие мобильность кода. Было определено обязательное ядро языка, которое должно быть реализовано на любой машине, включая самые маломощные. Кроме того, язык мог дополнительно включать в себя массу расширений; однако программист, использующий лишь конструкции, входящие в ядро, мог быть уверен, что его код будет работоспособен на любой стандартной реализации COBOL.
Программа на языке COBOL состоит из четырех разделов. Первый, раздел идентификации, позволяет задать имя программы и автора, а также включить некоторые дополнительные сведения о ней. Второй, раздел оборудования, описывает тип компьютера, на котором должна выполняться программа, а также отображение логических файлов ввода-вывода на физические.
Раздел данных, разумеется, содержит описания используемых структур данных, а раздел процедур - собственно выполняемый код. (Несмотря на название раздела, использование процедур в COBOL применяется редко, а в минимальной версии языка подпрограммы могут быть вообще не реализованы; функции в привычном нам виде вообще отсутствуют).
Среди управляющих структур имеются вездесущий оператор
GO TO, условный
IF ... THEN ... ELSE в несколько урезанном виде и многоцелевая инструкция
PERFORM, при помощи которой можно организовать цикл либо вызвать подпрограмму. Как правило, все переменные глобальны и описываются в разделе данных.
Структуры данных разнообразнее. Здесь имеются обычные однородные массивы, а также неоднородные, называемые записями. При этом элементом однородного массива может быть запись, и наоборот. Запись также определяет структуру внешних файлов, средства для работы с которыми весьма развиты, включая операции ввода-вывода для последовательных, индексно-последовательных и файлов прямого доступа, а также сортировки данных. Имеется также генератор отчетов, позволяющий получать достаточно сложные структуры отчетов.
В завершение приведу небольшой фрагмент программы на COBOL:
DATA DIVISION.
FILE SECTION.
FD INP-DATA LABEL RECORD IS OMITTED.
01 ITEM-PRICE.
02 ITEM PICTURE X(30).
02 PRICE PICTURE 9999V99.
02 FILLER PICTURE X(44).
PROCEDURE DIVISION.
START.
OPEN INPUT INP-DATA AND OUTPUT RESULT-FILE.
READ-DATA.
READ INP-DATA AT END GO TO PRINT-LINE.
ADD PRICE TO TOT.
ADD 1 TO COUNT.
MOVE PRICE TO PRICE-OUT.
MOVE ITEM TO ITEM-OUT.
WRITE RESULT-LINE FROM ITEM-LINE.
GO TO READ-DATA.
PRINT-LINE.
MOVE TOT TO SUM-OUT