Сергей Свердлов*
Мало найдется в компьютерном деле тем, которые вызывали бы столько противоречивых суждений и споров, как сравнение языков программирования.
Когда я был молодым аспирантом, для нас организовали курсы по Фортрану. Тогда вовсю разворачивались ЕС ЭВМ, на которых Фортран стал одним из основных языков. К тому времени я немного знал Алгол-60, написал на нем несколько небольших программ. Посещая эти курсы, я все время пытался понять, чем Фортран лучше Алгола. Чтобы разрешить свои сомнения, я приставал с вопросами к старшим товарищам. Никто из них тезис о преимуществе Фортрана не подверг сомнению, причем в его пользу можно было услышать самые разные аргументы. Запомнился такой, например, ответ: “В Фортране массивы могут быть семимерными, а в Алголе - нет”. В этом ответе - все. И желание скрыть поверхностное знакомство с обоими языками. И выдвижение на передний план вещей второстепенных. И смешение понятий собственно языка и его конкретной реализации. С тех пор, между прочим, мне так ни разу и не пришлось употребить семимерные массивы.
В то же время не вполне аргументированный подход к сравнению языков можно понять и в известной мере оправдать. В реальной работе не так уж часто возникает потребность активно работать одновременно на разных языках.
Но при этом немалую роль играют пропаганда и мода. Сейчас, например, мы все являемся свидетелями массированной пропагандистской кампании, развернутой вокруг языка Java. Впервые язык программирования выводится на рынок такими же маркетинговыми приемами, что и традиционные товары.
Какой язык лучше?
Существует ли ответ на этот вопрос? Скорее всего, нет. Как минимум следует кое-что уточнить. Лучше для чего? Для какой сферы применения? Для какой задачи? Для какого проекта? Для какого программиста, наконец? И уж, конечно, оценивая тот или иной язык, хорошо бы опираться на хоть сколько-нибудь объективные критерии.
Мне несколько раз встречались публикации, в которых предпринималась попытка оценить языки по определенному набору критериев. За основу берутся, как правило, экспертные оценки.
Но это достаточно субъективно. К тому же теперь за оценки беспристрастных экспертов, бывает, выдается откровенная реклама. И еще один момент. Когда вы, например, слышите, что язык Java проще Си++, то спрашивается: а насколько проще? Или, может, во сколько раз? Задавшись однажды таким вопросом, я пришел к идее небольшого исследования. Его результатами хочу теперь поделиться.
Какой язык проще?
Не будем пытаться выстроить критерий, вычисляемый в качестве оценки “сложности языка программирования”. Займемся тем, что в любом языке, начиная с Алгола-60, строго формализовано - описаниями синтаксиса языков, оценкой объема этих описаний.
Конечно, синтаксис - это лишь внешняя форма, но он играет важную роль. Одновременно синтаксические единицы являются и основными смысловыми понятиями любого языка: модуль, класс, блок, функция, процедура, метод, оператор, выражение. Ну а что, как не система понятий и их количество, может характеризовать сложность языка?
Те, кто знаком с устройством компиляторов, знают, что именно синтаксический анализатор представляет собой как бы скелет всего компилятора.
Итак, в качестве меры сложности языка программирования будем рассматривать количественные характеристики формализованного описания его синтаксиса.
Объекты
Не вздрагивайте. Я не буду говорить про инкапсуляцию, наследование и полиморфизм. Речь пойдет о языках программирования, которые станут объектами нашего исследования. Во-первых, поскольку мы собрались оценивать только сложность, мы должны взять языки, сопоставимые в функциональном отношении. Во-вторых, нужно, чтобы способы описания синтаксиса рассматриваемых языков можно было привести к некоторому общему знаменателю. И наконец, желательно, чтобы к обсуждаемым языкам существовал общественный интерес. Предметом нашего исследования будут: Алгол-60, Паскаль (в версии Н. Вирта), Си (K&R), Ада, Модула-2, ряд версий Турбо (Борланд)-Паскаля, Объектный Паскаль (Дельфи), Си++, Оберон, Оберон-2, Java. Коротко об “участниках” соревнований.
Никлаус Вирт -создатель
Паскаля, Модулы и Оберона
Алгол-60 Это первый язык, синтаксис которого был описан формально с помощью специально для этого созданной нотации - формул Бэкуса - Наура (ФБН). Вот как в этой нотации выглядит определение идентификатора:
<идентификатор> ::= <буква>| <идентификатор><буква> | <идентификатор><цифра>.
В 60-х - начале 70-х Алгол был самым популярным языком в СССР. Сейчас его не используют. Но здесь он рассматривается, так как по сути является родоначальником всех обсуждаемых языков. Нам же будет интересно проследить, насколько усложнены (или, может, упрощены) современные языки по сравнению с Алголом.
Андерс Хейльсберг -разработчик
Турбо-Паскаля и Дельфи
Паскаль Прямой потомок Алгола. При описании синтаксиса Паскаля его автор Н. Вирт использовал ФБН, добавив в нотацию скобки { и }, означающие повторение заключенной внутри них конструкции. Паскаль в оригинальной авторской версии не содержит средств раздельной компиляции - модулей, разнообразных числовых типов, строк переменной длины и многого из того, что добавлено в известные реализации.
Си Этот язык достаточно традиционен. Приобрел популярность благодаря остроумным решениям, сделавшим запись программы на Си весьма компактной. Не накладывая на программиста особых ограничений, он дает возможность для разнообразных трюков, чем тоже многим импонирует.
При описании языка использована нотация, в принципе эквивалентная ФБН.
Каноническим считается описание, данное в книге создателя языка Д. Ритчи с соавторами. Материалами, взятыми из русского издания этой книги (Керниган Б., Ритчи Д., Фьюэр А. Язык программирования Си. Задачи по языку Си. М., Финансы и статистика, 1985) мы и будем пользоваться, говоря о языке Си.
Ада Официальный язык программирования американских военных. Происходит от Паскаля, но заметно сложнее его (интересно будет узнать, насколько). Описан очень точно и строго. Для описания синтаксиса использован вариант ФБН.
Модула-2 Язык, который должен был заменить Паскаль, устранив основное его ограничение - отсутствие модульности. Но полноценной замены не получилось. В тот момент, когда нужно было обеспечить модульность в своих системах программирования, компания Borland решила, что выгоднее не переходить на Модулу-2, а добавить новые элементы в Паскаль. Тем не менее известно, что Модула-2 использовалась и используется в проектах, где важнейшую роль играет надежность. Средства межмодульного контроля Модулы-2 заметно совершеннее аналогичных возможностей Турбо-Паскаля и Си.
При описании синтаксиса языка Н. Вирт опирался на так называемые расширенные формулы Бэкуса - Наура (РФБН). В нотацию введены средства выражения итерации и задания необязательных частей, что позволяет в большинстве случаев обходиться без рекурсивных определений. Идентификатор теперь определяется так: идентификатор = буква { буква | цифра }.
Фигурные скобки показывают, что заключенной в них конструкции может вовсе не быть либо она может может повторяться сколько угодно раз. Получается гораздо проще и удобней.
При наших измерениях синтаксические правила для всех исследуемых языков были преобразованы именно в РФБН.
Турбо-Паскаль и Объектный Паскаль Компилятор Турбо-Паскаль, разработанный Андерсом Хейльсбергом, был выпущен в продажу фирмой Borland в 1983 г. Эта версия уже содержала расширения языка, хотя и небольшие. В последующих выпусках расширений становилось все больше: встроенная графика (версия 3.0), от которой потом отказались, модули (4.0), средства ООП (объектно-ориентированное программирование; версия 5.5) и т. д. Начиная с версии 7.0 язык стал называться Borland-Паскаль, а с появлением системы Дельфи был переименован в Объектный Паскаль. Сейчас входной язык Дельфи по сравнению со стандартным Паскалем содержит очень много синтаксических расширений. Существует даже мнение, с которым я вполне солидарен, будто именно отсутствием жесткой привязки к стандарту языка объясняется тот факт, что визуальная среда программирования была создана компанией Borland сначала для Паскаля, а уж потом для Си. Но, думаю, не менее, а скорее более важная причина кроется в другом: разработкой Дельфи занимался выдающийся программист A. Хейльсберг.
Бъярн Строуструп придумал Си++
Си++ Первоначальное название “Си с классами”. В язык Си Бъярн Строуструп ввел средства ООП и ряд других добавлений. До последнего времени это самый модный язык. Сейчас принято считать, что Си++ сложноват и не слишком надежен. Многие говорили об этом с самого начала.
Оберон Разработан Н. Виртом в 1987 г. Представляет собой существенно упрощенный вариант Модулы-2, в который добавлены расширяемые записи - основной механизм ООП. Язык необычайно прост. При этом сохраняет универсальность и в функциональном отношении не уступает другим языкам.
Ханспетер Мёссенбёк -соавтор
Н. Вирта по языку Оберон-2
Оберон-2 В 1992 г. были приняты расширения языка Оберон, предложенные молодым коллегой Н. Вирта Ханспетером Мёссенбёком. В язык введены так называемые связанные процедуры - аналог виртуальных методов в других языках.
Java Самый молодой и самый обсуждаемый ныне язык. Являясь непосредственным наследником Си++, отличается от него отсутствием некоторых потенциально ненадежных механизмов, а также тем, что в нем устранены любые не относящиеся к ООП средства. Объекты и ничего кроме объектов! Говорят, что Java - простой язык. Но стоит заглянуть в его официальное описание (James Gosling, Bill Joy, Guy Steele. The Java Language Specification Version 1.0), как возникает масса сомнений. Тяжелый (в прямом и переносном смысле) 700-страничный документ насыщен многословными и громоздкими определениями.
Программа
Итак, круг исследуемых языков определен. Теперь сформулируем порядок проведения подсчетов:
- описание синтаксиса каждого из сравниваемых языков записывается в нотации РФБН в текстовые файлы;
- с помощью специальной программы эти файлы обрабатываются с целью подсчета количественных характеристик описаний синтаксиса.
Программа, используемая при выполнении измерений, устроена на тех же принципах, что и компиляторы языков программирования. И это не удивительно. Ведь РФБН-нотация сама представляет собой язык со своей лексикой и синтаксисом. Программа проверяет корректность обрабатываемых описаний. В ходе анализа строятся таблицы и ведутся подсчеты.
Критерии
Правила, записанные с помощью РФБН (как и текст на языке программирования), состоят из отдельных элементов - лексем. Лексемами являются определения понятий, называемые в теории формальных языков нетерминальными символами или просто нетерминалами. Например, в правиле последовательность операторов = оператор {“;” оператор}. нетерминалами являются последовательность операторов и оператор. Терминальные символы - это те знаки, из которых и состоит в конечном счете (terminal - конечный, заключительный) программа. При записи в РФБН терминальные символы ставятся в кавычки. В приведенном примере один терминал: “;”. Терминальные символы - это тоже лексемы РФБН. Наконец, к числу лексем относятся специальные знаки, используемые в самих РФБН. В правиле о последовательности операторов это знак равенства, фигурные скобки и точка в конце.
Общее число лексем в описании синтаксиса языка может служить обобщенной характеристикой размера этого описания. Число лексем использовать в качестве меры объема гораздо лучше, чем, скажем, число знаков в описании. В этом случае значение нашего критерия не будет зависеть от того, на каком языке (русском, английском) или какими конкретно словами названы нетерминалы - понятия языка. Число различных нетерминалов - следующая характеристика, которую мы будем определять путем вычислений. Количество используемых для описания языка понятий - несомненно важнейшее свойство, ведь именно от него зависит легкость освоения этого языка. Можно заметить, что число нетерминалов должно быть равно числу правил в описании синтаксиса, поскольку для каждого понятия должно быть ровно одно правило.
Набор и количество различных терминальных символов языка, упомянутых в синтаксических формулах, характеризуют лексику языка - набор знаков и специальных символов.
Во всех обсуждаемых нами языках существуют служебные слова, которые могут употребляться только в строго определенном смысле. Вообще-то программист должен знать их наизусть. Подсчет количества служебных слов позволит оценить объем зубрежки.
Линия Вирта
Так называется линия языков, начинающаяся от Алгола-60 и продолженная Паскалем, Модулой-2, Обероном и Обероном-2, автором которых является Никлаус Вирт. Сам Вирт этой линии неуклонно придерживается: наращивание мощи языка без его усложнения. Паскаль намного богаче Алгола, но не сложнее его. Модула существенно мощнее и совершеннее Паскаля, но проще. Оберон обогатил Модулу средствами ООП - расширяемыми записями и при этом не только не стал более сложным, но заметно упрощен.
Удивительно, но Оберон-2 оказался проще Оберона, расширением которого является. В отношении размера определения синтаксиса это безусловно. Да и по существу нововведения Оберона-2 оформлены очень экономно. Кроме того, авторы языка объединили отдельные правила для каждой разновидности операторов в одно правило для нетерминала “Оператор”. То же сделано в отношении правил для типов. По-другому, более компактно, определен синтаксис некоторых конструкций. И хотя получившееся упрощение отчасти формально, экономия понятий - это именно то, к чему и следует стремиться, как заметил еще У. Оккам почти 700 лет назад.
Кроме того, очень лаконичны и полные тексты спецификаций языков Н. Вирта. Сравнивая авторское описание Паскаля, тоже очень небольшое, и описания языков Оберон и Оберон-2, мы можем видеть, как маэстро совершенствовал свое мастерство. Я очень рекомендую специалистам по Си++ и java прочитать спецификацию Оберона, даже если они не собираются этот язык использовать. Всего 20 страниц! Но это документ, необходимый и достаточный для реализации языка. Сравните с аналогичного назначения книгами Б. Строуструпа и Д. Гослинга по Си++ и Java.
Джеймс Гослинг -“отец” языка Java
Линия Borland
Берет начало от виртова Паскаля, а дальше - “все выше, и выше, и выше...”. Версии Турбо - Borland - Объектного Паскаля становятся сложнее и сложнее. И по-другому просто не может быть, поскольку, избрав однажды путь расширения старого языка с сохранением обратной совместимости, вы можете его только усложнять. Отказаться от чего-либо уже невозможно. Недаром по линии Borland идет монотонный рост значений всех без исключения критериев.
Что же в результате? Из простого и изящного Паскаля получился язык, приближающийся по сложности к языку Ада. По большому счету Паскаль в версии Борланда (Inprise) уже не может считаться общемировым языком программирования. Это фирменный язык одной не очень большой американской компании. В таком смысле он ничем не отличается от Бейсика, языка другой, правда более крупной, фирмы. Отсутствие переносимости даже делает не вполне правомерным сравнение этого языка с Си, Си++, Java, Модулой, Обероном и Адой.
От простого к сложному
Одним из неожиданных для меня результатов измерений стало то, что Си оказался не простым, а очень простым языком. По некоторым параметрам он даже проще Оберона. Недаром же в свое время он завоевал такую популярность. В самом деле Си весьма гармоничен. И хотя поощряемый им стиль принимают не все, но в изяществе языку не откажешь. Что стоит только знаменитое ++. Интересно, кстати, что Си обладает одним из самых обширных наборов терминальных символов. Вот они, эти “штучки”: ++, +=, && и т. д.
Но ничего хорошего в смысле простоты нельзя сказать про Си++. Сейчас чрезмерная сложность этого языка стала общепризнанной. Так что неожиданностей наши измерения не дали. Разве что можно заявить о том, что Си++ сложнее Си практически вдвое. Вообще, если Си - конструкция достаточно цельная, то Си++ представляется весьма противоестественным соединением языка с незамаскированными понятиями низкого уровня и высокоуровневой концепции объектов. К тому же мне кажется, что популярность Си возникла естественным путем, а Си++ - искусственно “раскрученный” язык.
Голый король?
А теперь обсудим характеристики “самого современного, самого объектно-ориентированного и очень простого” языка Java. Но что это? Судя по вычисленным нами критериям, Java не только не может считаться простым, но является одним из самых сложных языков, более сложным, чем Си++, и вдвое более сложным, чем Оберон.
Но может быть, сопоставление с тем же Обероном некорректно? Ведь, наверное, Java все же более богатый язык, чем этот ваш Оберон? Ничего подобного! В Java есть всего две существенные вещи, которых нет в Обероне: встроенная многопоточность и обработка исключений. Целесообразность включения средств параллельного программирования непосредственно в язык подвергается сомнению многими специалистами. Это могло бы решаться на уровне библиотек. К тому же тот механизм, который реализован в Java, - решение отнюдь не самое удачное. Я с содроганием вспоминаю свои ощущения от чтения главы из спецификации языка Java, посвященной потокам. Что касается обработки исключений, то, встроенная в Java, она, во-первых, усложняет язык, а во-вторых, все время напрягает программиста, вынуждая его употреблять всякие лишние слова, даже если он того не хочет. Полноценно восстановиться после по-настоящему серьезной, исключительной ситуации удается редко, разве что вместо системного сообщения об ошибке можно выдать собственное. Простые соглашения о флагах вполне могли бы решать те же задачи.
Сравнение объема синтаксиса языков
Зато в маленьком Обероне есть и полноценные записи (объекты), и нормальные многомерные массивы, а не только указатели на них. Имеются в Обероне и привычные строки с нулем на конце, которые являются просто массивами символов, а никакими не объектами, а значит, не требуют специальных средств для манипуляций. А еще есть множества, вложенные процедуры, параметры-переменные. Вопрос о последних особенно интересен. В языках Си и Си++, как известно, параметры всегда передаются по значению, т.е. параметров-переменных тоже нет, но зато можно передать в качестве параметра адрес, что полностью решает вопрос. В Java же в борьбе с адресной арифметикой вместе с водой выплеснули и ребенка: параметры-переменные отсутствуют, адреса тоже. В качестве компенсации предлагается нечто несуразное под названием классы-фантики (wrapper classes).
Вопреки пропаганде Java содержит мало чего-либо действительно нового. Та же концепция виртуальной машины - первое, что приходит в голову, если задуматься о многоплатформности. Лет двадцать пять назад это было удачным и свежим решением. Сейчас разработаны существенно более эффективные подходы.
С использованными при подсчетах описаниями языков программирования можно ознакомиться на странице http:// www.uni-vologda.ac.ru/CS/Syntax/.
-----
* В подготовке материалов, использованных в статье, участвовали С. Командирова, И. Назарова, Т. Перешивалова, А. Полысаев, Н. Скотникова, М. Шулепина, Н. Шумилова.
С автором можно связаться по адресу: c3c@uni-vologda.ac.ru.
Количество элементов в языке
Элементы | A-60 | Паскаль | ТП2 | ТП5 | ТП5.5 | ТП6 | ОП | М2 | О | О2 | Си | Си++ | Java | Ада |
Лексемы | 1085 | 1012 | 1184 | 1331 | 1410 | 1488 | 1825 | 887 | 765 | 726 | 917 | 1662 | 1771 | 2206 |
Нетерминалы | 119 | 110 | 124 | 127 | 135 | 143 | 180 | 70 | 62 | 43 | 917 | 126 | 174 | 226 |
Терминалы | 92 | 84 | 87 | 87 | 87 | 89 | 90 | 88 | 90 | 91 | 123 | 131 | 121 | 102 |
Служебные слова | 25 | 35 | 42 | 48 | 52 | 55 | 83 | 39 | 32 | 34 | 27 | 47 | 48 | 63 |
ТП - Турбо-Паскаль; ОП - Объектный Паскаль; М - Модула; О - Оберон.