Исторический обзор, классификация и характеристика языков программирования

Тип работы:
Реферат
Предмет:
Программирование


Узнать стоимость

Детальная информация о работе

Выдержка из работы

Министерство образования Республики Беларусь

Учреждение образования

Гомельский государственный университет имени Ф. Скорины

Реферат

на тему:

Исторический обзор, классификация и характеристики языков программирования

Гомель 2006

Содержание

Введение

1. Понятия зыка программирования

1.1 Машинный код

1.2 Алгоритм и программа

1.3 Что такое язык программирования

1.4 Уровни языков программирования

1.5 Компилируемые и интерпретируемые языки

2. Классификация языков программирования

3. Исторический обзор языков программирования

4. Характеристики языков программирования

4.1 Элементы объектной модели

4.2 Характеристики языков программирования с точки зрения элементов объектной модели

Приложение. Популярность языков программирования

Заключение

Литература

Введение

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

Один из подходов, помогающий провести обзор языков программирования, -- подход исторический. Действительно, на первый взгляд все выглядит очень просто. Сначала было программирование в машинных кодах, когда программист являлся единственным посредником между остальными смертными и Машиной -- гигантским «ламповым монстром», занимающим если не целое здание, то почти целый этаж. Затем появились мнемонические представления машинного кода, ассемблер и, наконец, макроассемблер. В конце 50-х возникли языки формульного программирования, из которых наиболее замечательным был Фортран, затем (в 60-х) центр тяжести стал понемногу смещаться к нечисленным методам -- появился АЛГОЛ. Еще немного, и к 70-м годам произошла структурная революция -- АЛГОЛ-W и, наконец, Паскаль. Далее настала очередь «модульного» программирования -- Модула и Модула-2. Приблизительно в это же время рождается знаменитый язык Си, идет новая революция логического программирования -- в моде ПРОЛОГ и экспертные системы. Пентагон проводит свой знаменитый конкурс, на котором побеждает Ада, а Япония заявляет о проекте машин пятого поколения, основанных на SmallTalk. В результате происходит объектно-ориентированная революция, появляются С++, Оберон, Eiffel и Модула-3.

Общие тенденции развития языков программирования при историческом подходе проследить вполне возможно, однако изложение получается сумбурным и путаным. Удивляться тут нечему -- ладно если бы произошла, например, структурная революция: программистская общественность присягнула на верность программированию без «goto» и прощай Фортран! Но нет, и Фортран сейчас «в ходу», а если вспомнить об его преуспевших детях Basic и Visual Basic, то придется признать, что «некрологи» Фортрана более чем двадцатилетней давности выглядят в наше время как забавный исторический курьез. Хотя верно и то, что тех версий языка Фортран, что были четверть века назад, уже не осталось, да и машин, на которых с ними работали, сейчас найдешь разве что в музее. Язык Паскаль также, по сравнению со многими другими языками, сейчас уже не молод, но это не мешает ему оставаться одним из наиболее популярных языков и в наши дни. А Кобол? У него еще более преклонный возраст, а если посмотреть по конференциям на Internet -- окажется, что очень много работ и сейчас проводится на Коболе.

Другой возможный классификационный критерий языков программирования -- это революционные идеи программирования, воплотившиеся в соответствующих решениях: структурное программирование, модульное программирование, объектно-ориентированное программирование. Однако и тут четкой классификации не получится. К примеру, Паскаль возник как «продукт» структурной революции, удачно впитал в себя идеи революции «модульной», и сегодня существует практически на всех компьютерных платформах в объектно-ориентированных воплощениях. Другой пример: приверженцы языка С++, как правило, самым важным его достоинством называют объектно-ориентированное программирование. Однако было бы неверно считать, что С++ стал популярным только благодаря объектам -- как и объектно-ориентированный Паскаль, С++ является языком гибридным. Применение объектно-ориентированной парадигмы при работе на нем совсем не обязательно, и многие программисты в практической работе этими возможностями как в С++, так и в объектно-ориентированном Паскале не пользуются. Точно так же, работая с современными компиляторами языка Паскаль, например с широко известными Borland Pascal/Turbo Pascal (Borland) 7.0 для IBM PC или Think Pascal (Symantec) для компьютеров Macintosh, можно в явном виде не пользоваться модульными возможностями, оформляя исходный код программы почти в полном соответствии со стандартным Паскалем. Ошибочный подход, скажет иной поклонник прогресса. Однако исходный код, соответствующий стандарту, будет обладать высокой переносимостью на разные платформы. Могут быть и иные резоны как для стандартных, так и для гибридных подходов. Видимо, поэтому разработчики объектно-ориентированного языка Модула-3 сделали принцип «гибридности» одним из основных в своем языке. С другой стороны, существует большая группа чистых объектно-ориентированных языков, где объектно-ориентированная парадигма является обязательной. Примером такого языка может служить Dee.

Казалось бы, еще один удобный классификационный признак -- популярность языка: чем язык популярней -- тем он лучше, так может, недолго думая, и разбить все языки на «плохие» и «хорошие»? Однако хорошо известно, что коммерческий успех того или иного продукта не является объективной оценкой качества. Мода -- преходяща, особенно если ее приход стимулируется громкой рекламой и значительными капиталовложениями. И чем популярней язык, тем больше споров вокруг него, чем больше у него сторонников -- тем больше и противников. Так, в самое ближайшее время много споров можно будет услышать об языке Java. Не вызовет ли приверженность этого языка принципам объектно-ориентированного программирования оттока его сторонников -- и тех, кто не стремится использовать объектно-ориентированное программирование на практике, и тех, кто считает, что в ряде современных языков (например в С++ или в Eiffel) эти принципы реализованы полнее?

И, наконец, еще один критерий -- уровень языка. Традиционно к языкам низкого уровня относят ассемблеры, а к языкам высокого уровня все остальные универсальные языки программирования, впрочем, для таких языков, как Форт (FORTH), Си и т. д., иногда выделяется некий «промежуточный» уровень. Помимо этого, делались неоднократные попытки выделить какой-либо язык или группу языков на «сверхвысокий» уровень, например для макросредств электронных таблиц. Не нужно вдаваться в детали, чтобы почувствовать всю условность и этой классификации. Тем более, если учесть, что большинство реализаций современных языков программирования высокого уровня имеют богатые низкоуровневые возможности. Скажем, Inline-директива, позволяющая записывать в исходном тексте машинные коды, или встроенный ассемблер, как, например, это сделано в Borland Pascal. Отметим, что, как правило, встроенный ассемблер гораздо удобнее -- запись в кодах на его фоне выглядит анахронизмом. При необходимости программирования какого-либо фрагмента программы на низком уровне можно применять и обычный «не встроенный» ассемблер. В общем случае в современных языковых средах вполне возможно программировать разные модули одной и той же программы на разных языках, например и на нескольких языках высокого уровня. Отметим, однако, что на практике этого лучше не делать без особых причин.

Весьма проблематична классификация языков и по названию. Например, Н. Вирт заявляет, что недостатки концепций языка Паскаль преодолеваются в языках Модула-2 и Оберон: «Назови я эти языки Паскаль-2 и Паскаль-3… их эволюционная линия была бы очевидна». Напрашивается вопрос -- что есть язык, а что есть диалект языка: например, стандартный Паскаль, расширенный Паскаль (Extended Pascal) и Turbo Pascal -- три разных языка или три диалекта Паскаля?

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

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

1.1 Машинный код

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

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

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

1.2 Алгоритм и программа

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

1.3 Что такое язык программирования

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

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

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

Процесс поиска ошибки в программе называется тестированием, процесс устранения ошибки — отладкой.

1.4 Уровни языков программирования

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

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

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

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

Языки программирования высокого уровня имеют следующие особенности:

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

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

ь Конструкция команд (операторов) отражает содержательные виды обработки данных и задаются в удобном для человека виде;

ь Используется аппарат переменных и действия над ними;

ь Поддерживается широкий набор типов данных

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

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

1. 5. Компилируемые и интерпретируемые языки

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

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

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

Кратко говоря, компилятор переводит программу на машинный язык сразу и целиком, создавая при этом отдельную программу, а интерпретатор переводит на машинный язык прямо во время исполнения программы.

Разделение на компилируемые и интерпретируемые языки является несколько условным. Так, для любого традиционно компилируемого языка, как, например, Паскаль, можно написать интерпретатор. Кроме того, большинство современных «чистых» интерпретаторов не исполняют конструкции языка непосредственно, а компилируют их в некоторое высокоуровневое промежуточное представление (например, с разыменованием переменных и раскрытием макросов).

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

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

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

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

2. Классификация языков программирования

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

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

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

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

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

Основные концепции модульного программирования:

· каждый модуль реализует единственную независимую функцию;

· каждый модуль имеет единственную точку входа и выхода;

· каждый модуль имеет единственную точку входа и выхода;

· размер модуля по возможности должен быть минимизирован;

· каждый модуль может быть разработан и закодирован различными членами бригады программистов и может быть отдельно протестирован;

· вся система построена из модулей;

· модуль не должен давать побочных эффектов;

· каждый модуль не зависит от того, как реализованы другие модули.

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

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

Концепция модульного программирования реализована в ряде языков, таких как Modula 2, Turbo Pascal 5.0 и выше, C, Python, Perl.

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

Принципиально иное направление в программировании связано с парадигмами непроцедурного программирования. К ним можно отнести объектно-ориентированное и декларативное программирование.

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

Из языков объектного программирования, популярных среди профессионалов, следует назвать прежде всего C++, для более широкого круга программистов предпочтительны среды типа Delphi и Visual Basic.

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

Декларативные языки подразделяются на два класса: функциональные и логические.

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

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

В лямбда-исчислении Черча функция записывается в виде l (x1,x2, …, xn). fn

В Лиспе лямбда-выражение имеет вид:

(LAMBDA (x1,x2, …, xn). fn).

Символ LAMBDA означает, что мы имеем дело с определением функции. Символы xi являются формальными параметрами, они образуют список, называемый лямбда-списком; fn — тело функции, которое может иметь произвольную форму, допускаемую интерпретатором Лиспа. Телом функции может быть константа или композиция из вызовов функций.

Программы на языках логического программирования выражены как формулы математической логики, а компилятор пытается получить следствия из них. Так же как в функциональном программировании, программист остается в неведении о методах, применяемых при вычислении, и последовательности исполнения элементарных действий. Большая часть ответственности за эффективность вычислений в логическом и функциональном программировании перекладывается на «плечи» транслятора используемого языка программирования.

Все сказанное выше можно отобразить следующей схемой:

50

Приведем другие классификации языков программирования.

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

A. Простые операционные модели (языки, основанные на конечных автоматах, машине Тьюринга);

B. Аппликативные модели (языки на основе лямбда-исчисления Чёрча, системы комбинаторов Карри, чистого Лиспа);

C. Модели фон Неймана (традиционные языки программирования).

Джон Устерхаут предложил принцип классификации языков, в соответствии с которым высокоуровневые языки делятся на языки системного программирования и на скриптовые.

Скриптовый язык (англ. scripting language, также называют язык сценариев) -- язык программирования, разработанный для записи «сценариев», последовательностей операций, которые пользователь может выполнять на компьютере. Простые скриптовые языки раньше часто называли языками пакетной обработки (batch languages или job control languages). Сценарии всегда интерпретируются, а не компилируются.

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

Поскольку сценарии интерпретируются из исходного кода динамически при каждом исполнении, они выполняются обычно значительно медленнее готовых программ, оттранслированных в машинный код на этапе компиляции. Поэтому сценарные языки не применяются для написания программ, требующих оптимальности и быстроты исполнения. Но из-за простоты они часто применяются для написания небольших, одноразовых («проблемных») программ. Также, в плане быстродействия скриптовые языки можно разделить на языки динамического разбора (sh, command. com) и предварительно компилируемые (Perl). Языки динамического разбора считывают инструкции из файла программы минимально требующимися блоками, и исполняют эти блоки, не читая дальнейший код. Предкомпилируемые языки вначале считывают всю программу, компилируют её всю либо в машинный код, либо в какой-то внутренний формат, и лишь затем -- исполняют получившийся код.

Вегнер сгруппировал некоторые из наиболее известных языков высокого уровня в четыре поколения в зависимости от того, какие языковые конструкции впервые в них появились:

FORTRAN I

Математические формулы

ALGOL-58

Математические формулы

Flowmatic

Математические формулы

IPL V

Математические формулы

Первое поколение (1954−1958)

Второе поколение (1959−1961)

FORTRAN II

Подпрограммы, раздельная компиляция

ALGOL-60

Блочная структура, типы данных

COBOL

Описание данных, работа с файлами

Lisp

Обработка списков, указатели, сборка мусора

Третье поколение (1962−1970)

PL/I

FORTRAN+ALGOL+COBOL

ALGOL-68

Более строгий приемник ALGOL-60

Pascal

Более простой приемник ALGOL-60

Simula

Классы, абстрактные данные

· Потерянное поколение (1970−1980)

Языки созданные, но не выжившие. Например, PL/1, Malboge.

3. Исторический обзор зыков программирования

В 1830-е годы математик Чарльз Беббидж загорелся великолепной и (по тем временам) абсолютно сумасшедшей идеей построения «думающей» машины. Он решился на постройку полностью автоматического счетного устройства. Эта машина должна была выполнять одно сложение в секунду и работать без участия человека. Наверное, первым языком программирования нужно считать язык программирования для машины Беббиджа. Он предполагал, что его машина будет складывать и вычитать за одну секунду, умножать два пятидесятизначных числа за минуту, делить стозначное на пятидесятизначное тоже за минуту. Но он так и не достроил машину из-за нехватки средств (к тому времени вся машина целиком занимала огромную комнату) и рабочих. Своих денег Беббиджу не хватило, а никто другой не соглашался ему помочь, потому что никто не верил в эту затею с автоматической машиной. Ее даже называли «чудачеством Беббиджа». А первым программистом стала дочь лорда Байрона — Ада Лавлейс. Она помогала ему составлять программы для этой «чудо-машины».

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

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

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

В 1954 году в недрах корпорации IBM группой разработчиков во главе с Джоном Бэкусом был создан язык программирования Fortran. Значение этого события трудно переоценить. Это первый язык программирования высокого уровня. Впервые программист мог по-настоящему абстрагироваться от особенностей машинной архитектуры. Ключевой идеей, отличающей новый язык от ассемблера, была концепция подпрограмм. Напомним, что это современные компьютеры поддерживают подпрограммы на аппаратном уровне, предоставляя соответствующие команды и структуры данных прямо на уровне ассемблера, в 1954 же году это было совершенно не так. Поэтому компиляция Fortran’а была процессом отнюдь не тривиальным. Кроме того, синтаксическая структура языка была достаточно сложна для машинной обработки в первую очередь из-за того, что пробелы как синтаксические единицы вообще не использовались. Это порождало массу возможностей для скрытых ошибок, таких, например: в Фортране следующая конструкция описывает < цикл for до метки 10 при изменении индекса от 1 до 100> :

DO 10 I=1,100

Если же здесь заменить запятую на точку, то получится оператор присваивания:

DO10I = 1. 100.

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

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

В 1960 году был создан язык программирования Cobol. Он задумывался как язык для создания коммерческих приложений, и он стал таковым. На Коболе написаны тысячи прикладных коммерческих систем. Отличительной особенностью языка является возможность эффективной работы с большими массивами данных, что характерно именно коммерческих приложений. Популярность Кобола столь высока, что даже сейчас, при всех его недостатках (по структуре и замыслу Кобол во многом напоминает Фортран) появляются новые его диалекты и реализации. Так недавно появилась реализация Кобола, совместимая с Microsoft. NET, что потребовало внесения в язык некоторых черт объектно-ориентированного языка.

В 1960 году командой во главе с Петером Науром был создан язык программирования Algol. Этот язык дал начало целому семейству алгол-подобных языков (важнейший представитель -- Pascal). В 1968 году появилась новая версия языка. Она не нашла столь широкого практического применения, как первая версия, но была весьма популярна в кругах теоретиков. Язык был достаточно интересен, так как обладал многими уникальными на тот момент характеристиками.

В 1963 году в Дартмурском колледже был создан язык программирования BASIC (Beginners' All-Purpose Symbolic Instruction Code -- многоцелевой язык символических инструкций для начинающих). Язык задумывался в первую очередь как средство обучения и как первый изучаемый язык программирования. Он предполагался легко интерпретируемым и компилируемым. Надо сказать, что BASIC действительно стал языком, на котором учатся программировать (по крайней мере, так было еще несколько лет назад; сейчас эта роль отходит к Pascal). Было создано несколько мощных реализаций BASIC, поддерживающих самые современные концепции программирования (ярчайший пример -- Microsoft Visual Basic).

В 1964 году все та же корпорация IBM создала язык PL/1, который был призван заменить Cobol и Fortran в большинстве приложений. Язык обладал исключительным богатством синтаксических конструкций. В нем впервые появилась обработка исключительных ситуаций и поддержка параллелизма. Надо заметить, что синтаксическая структура языка была крайне сложной. Пробелы уже использовались как синтаксические разделители, но ключевые слова не были зарезервированы. В частности, следующая строка -- это вполне нормальный оператор на PL/1: IF ELSE=THEN THEN THEN; ELSE ELSE.

В силу таких особенностей разработка компилятора для PL/1 была исключительно сложным делом. Язык так и не стал популярен вне мира IBM.

Создание каждого из вышеупомянутых языков (за исключением Algol’а) было вызвано некоторыми практическими требованиями. Эти языки послужили фундаментом для более поздних разработок. Все они представляют одну и ту же парадигму программирования. Следующие языки пошли существенно дальше в своем развитии, в сторону более глубокого абстрагирования.

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

Pascal-подобные языки

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

Отрицательной чертой языка было отсутствие в нем средств для разбиения программы на модули. Вирт осознавал это и разработал язык Modula-2 (1978), в котором идея модуля стала одной из ключевых концепций языка. В 1988 году появилась Modula-3, в которую были добавлены объектно-ориентированные черты. Логическим продолжением Pascal и Modula являются язык Oberon и Oberon-2. Они характеризуются движением в сторону объектно- и компонентно-ориентированности.

C-подобные языки

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

В 1986 году Бьярн Страуструп создал первую версию языка C++, добавив в язык C объектно-ориентированные черты, взятые из Simula, и исправив некоторые ошибки и неудачные решения языка. C++ продолжает совершенствоваться и в настоящее время, так в 1998 году вышла новая (третья) версия стандарта, содержащая в себе некоторые довольно существенные изменения. Язык стал основой для разработки современных больших и сложных проектов. У него имеются, однако же, и слабые стороны, вытекающие из требований эффективности.

В 1995 году в корпорации Sun Microsystems Кеном Арнольдом и Джеймсом Гослингом был создан язык Java. Он наследовал синтаксис C и C++ и был избавлен от некоторых неприятных черт последнего. Отличительной особенностью языка является компиляция в код некоей абстрактной машины, для которой затем пишется эмулятор (Java Virtual Machine) для реальных систем. Кроме того, в Java нет указателей и множественного наследования, что сильно повышает надежность программирования.

В 1999—2000 годах в корпорации Microsoft был создан язык C#. Он в достаточной степени схож с Java (и задумывался как альтернатива последнему), но имеет и отличительные особенности. Ориентирован, в основном, на разработку многокомпонентных Интернет-приложений.

Языки Ada и Ada 95

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

Языки обработки данных

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

В 1957 году была предпринята попытка создания языка для описания математической обработки данных. Язык был назван APL (Application Programming Language). Его отличительной особенностью было использование математических символов (что затрудняло применение на текстовых терминалах; появление графических интерфейсов сняло эту проблему) и очень мощный синтаксис, который позволял производить множество нетривиальных операций прямо над сложными объектами, не прибегая к разбиению их на компоненты. Широкому применению помешало, как уже отмечалось, использование нестандартных символов как элементов синтаксиса.

В 1962 году появился язык Snobol (а в 1974 — его преемник Icon), предназначенный для обработки строк. Синтаксис Icon напоминает С и Pascal одновременно. Отличие заключается в наличии мощных встроенных функций работы со строками и связанная с этими функциями особая семантика. Современным аналогом Icon и Snobol является Perl --язык обработки строк и текстов, в который добавлены некоторые объектно-ориентированные возможности. Считается очень практичным языком, однако ему недостает элегантности.

В 1969 году был создан язык SETL — язык для описания операций над множествами. Основной структурой данных в языке является множество, а операции аналогичны математическим операциям над множествами. Полезен при написании программ, имеющих дело со сложными абстрактными объектами.

Объектно-ориентированные языки

Первым объектно-ориентрованным языком был язык Simula (1967). Этот язык был предназначен для моделирования различных объектов и процессов, и объектно-ориентированные черты появились в нем именно для описания свойств модельных объектов.

Популярность объектно-ориентированному программированию принес язык Smalltalk, созданный в 1972 году. Язык предназначался для проектирования сложных графических интерфейсов и был первым по-настоящему объектно-ориентированным языком. В нем классы и объекты -- это единственные конструкции программирования. Большим недостатком Smalltalk являются большие требования к памяти и низкая производительность полученных программ. Это связано с не очень удачной реализацией объектно-ориентированных особенностей. Популярность языков C++ и Ada 95 связана именно с тем, что объектно-ориентированность реализована без существенного снижения производительности.

Существует язык с очень хорошей реализацией объектно-ориентированности, не являющийся надстройкой ни над каким другим языком. Это язык Eiffel (1986). Являясь чистым языком объектно-ориентированного программирования, он, кроме того, повышает надежность программы путем использования < контрольных утверждений>.

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

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

Язык Оccam был создан в 1982 году и предназначен для программирования транспьютеров -- многопроцессорных систем распределенной обработки данных. Он описывает взаимодействие параллельных процессов в виде каналов -- способов передачи информации от одного процесса к другому. Отметим особенность синтаксиса языка occam -- в нем последовательный и параллельный порядки выполнение операторов равноправны, и их необходимо явно указывать ключевыми словами PAR и SEQ.

В 1985 году была предложена модель параллельных вычислений Linda. Основной ее задачей является организация взаимодействия между параллельно выполняющимися процессами. Это достигается за счет использования глобальной кортежной области (tuple space). Процесс может поместить туда кортеж с данными (то есть совокупность нескольких, возможно разнородных, данных), а другой процесс может ожидать появления в кортежной области некоторого кортежа и, после его появления, прочитать кортеж с возможным последующим его удалением. Заметим, что процесс может, например, поместить кортеж в область и завершиться, а другой процесс может через некоторое время воспользоваться этим кортежем. Таким образом обеспечивается возможность асинхронного взаимодействия. Очевидно, что при помощи такой модели может быть симулировано и синхронное взаимодействие. Linda -- это модель параллельных вычислений, она может быть добавлена в любой язык программирования. Существуют достаточно эффективные реализации Linda, обходящие проблему существования глобальной кортежной области с потенциально неограниченным объемом памяти.

Логические языки программирования

В начале 70-х годов Роберт Ковальский, в то время работавший в Эдинбурге, и Алан Колмероэ из Марселя разрабатывали сходные идеи и даже работали вместе в течение одного лета. В результате были сформулированы основные положения логического программирования, в 1972 году описан и реализован первый язык логического программирования — Пролог («программирование на языке логики» -- PROgramming in LOGic).

Функциональные языки программирования

Теория, положенная в основу функционального подхода родилась в 20-х -- 30-х годах XX века. В числе разработчиков математических основ функционального программирования можно назвать Мозеса Шёнфинкеля (Германия и Россия) и Хаскелла Карри (Англия), разработавших комбинаторную логику, а также Алонзо Чёрча (США) -- создателя -исчисления.

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

В связи с этим обстоятельством всё большую роль начинает играть типизация. В конце 70-х -- начале 80-х годов XX века интенсивно разрабатываются модели типизации, подходящие для функциональных языков. Появляется множество типизированных функциональных языков: ML (Робин Милнер, 1979, из ныне используемых диалектов известны Standard ML и Objective CAML), Scheme (1975), Hope, Miranda (Дэвид Тёрнер, 1985), Clean и многие другие. Вдобавок постоянно увеличивается число диалектов.

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

Ниже приведем схему «Эволюция языков программирования».

4. Характеристики языков программирования

ПоказатьСвернуть
Заполнить форму текущей работой