Если проследить короткую историю развития методов программирования, можно выделить две основные тенденции:
1. Перемещение акцентов от программирования отдельных деталей к программированию более крупных компонент.
2. Развитие и совершенствование языков программирования высокого уровня.
Большинство современных коммерческих программных систем существенно сложнее и объемнее, чем их предшественники. Рост сложности обусловил проведение серьезных исследований в области методологии проектирования программных систем; в частности, были разработаны методы декомпозиции, абстрагирования и построения иерархии. Кроме того, были созданы более выразительные языки программирования. Возникла тенденция перехода от процедурных языков программирования (описывающих действия) к декларативным языкам (описывающим ключевые абстракции проблемной области). Вэгнер следующим образом сгруппировал наиболее известные языки программирования высокого уровня в их поколения:
Первое поколение (вторая половина 50-ых годов):
FORTRAN I; ALGOL-58; Flowmatic; IPL V – все это математические формулы.
Второе поколение (начало 60-ых годов):
FORTRAN II: Подпрограммы, раздельная компиляция; ALCOL-60: Блочнаяструктура, типы данных; COBOL: Описание данных, работа с файлами; Lisp: Обработка списков, указатели, сборка мусора
Третье поколение (вторая половина 60-ых, до начала 70-ых):
PL/1 (FORTRAN + ALGOL + COBOL); ALGOL-68: более строгий приемник ALGOL-60; Pascal: более простой приемник ALGOL-60; Simula: классы, абстрактные данные
Потерянное поколение (1970-1980) – Много языков созданных, но мало выживших.
Под топологией языков программирования высокого уровня понимаются основные элементы языка программирования и их взаимодействие.
Топология языков первого и начала второго поколения. Для таких языков, как FORTRAN и COBOL, основным строительным блоком является подпрограмма. Программы, реализованные на таких языках, имеют относительно простую структуру, состоящую только из глобальных данных и подпрограмм. В процессе разработки можно логически разделить разнотипные данные, но механизмы языков практически не поддерживают такого разделения. Ошибка в какой-либо части программы может иметь далеко идущие последствия, так как область данных открыта всем подпрограммам. В больших системах трудно гарантировать целостность данных при внесении изменений в какую-либо часть системы. В процессе эксплуатации уже через короткое время возникает путаница из-за большого количества перекрестных связей между подпрограммами, запутанных схем управления, неясного смысла данных, что угрожает надежности системы и определенно снижает ясность программы.
Топология языков позднего второго и раннего третьего поколения. Начиная с середины 60-х годов стали осознавать роль подпрограмм как важного промежуточного звена между решаемой задачей и компьютером. Использование подпрограмм как механизма абстрагирования имело три существенных последствия. Во-первых, были разработаны языки, поддерживавшие разнообразные механизмы передачи параметров. Во-вторых, были заложены основания структурного программирования, что выразилось в языковой поддержке механизмов вложенности подпрограмм и в научном исследовании структур управления и областей видимости. В-третьих, возникли методы структурного проектирования, стимулирующие разработчиков создавать большие системы, используя подпрограммы как готовые строительные блоки.
Топология языков конца третьего поколения. Для решения задач программирования начал развиваться новый важный механизм структурирования. Разрастание программных проектов означало увеличение размеров и коллективов программистов, а, следовательно, необходимость независимой разработки отдельных частей проекта. Ответом на эту потребность стал отдельно компилируемый модуль, который сначала был просто более или менее случайным набором данных и подпрограмм. В такие модули собирали подпрограммы, которые, будут изменяться совместно. В большинстве языков этого поколения, хотя и поддерживалось модульное программирование, но не вводилось никаких правил, обеспечивающих согласование интерфейсов модулей. Программист, сочиняющий подпрограмму в одном из модулей, мог, например, ожидать, что ее будут вызывать с тремя параметрами: действительным числом, массивом из десяти элементов и целым числом, обозначающим логическое значение. Но в каком-то другом модуле, вопреки предположениям автора, эта подпрограмма могла по ошибке вызываться с фактическими параметрами в виде: целого числа, массива из пяти элементов и отрицательного числа. Аналогично, один из модулей мог завести общую область данных и считать, что это его собственная область, а другой модуль мог нарушить это предположение, свободно манипулируя с этими данными.
5. Интерпретаторы и компиляторы. «За» и «против». Структура. Понятие Байт-кода (P-Code) в языке Java. Языки 4GL.
Для преобразования операторов исходного языка программирования в машинные коды используются программы-трансляторы. Существует два больших класса программ-трансляторов: компиляторы и интерпретаторы. Компиляторы – это программы, которые преобразуют исходные тексты программ, написанные на языке программирования высокого уровня, в программу на машинном языке. Полученный код называемый исполняемой программой, записывается в память микропроцессора и далее его можно устанавливать и запускать на нужном компьютере без дополнительных преобразований. Интерпретаторы также преобразуют код, написанный на языке программирования высокого уровня, но при использовании интерпретатора в память микропроцессора записывается исходный текст программы, а трансляция производится построчно, при считывании очередного оператора. Для того чтобы программа была «понятна» компьютеру, на котором предполагается исполнять высокоуровневый код непосредственно, на нем также должна работать программа интерпретации.
Подавляющее большинство интерпретаторов действует так, что исполняет исходную программу последовательно, по мере ее поступления на вход интерпретатора. При таком порядке работы интерпретатора проявляется существенная особенность, которая отличает его от компилятора, — если интерпретатор исполняет команды по мере их поступления, то он не может выполнять оптимизацию исходной программы. Следовательно, фаза оптимизации в общей структуре интерпретатора будет отсутствовать. В остальном же она будет мало отличаться от структуры аналогичного компилятора. Следует только учесть, что на последнем этапе — генерации кода — машинные команды не записываются в объектный файл, а выполняются по мере их порождения.
Естественно, что быстродействие интерпретаторов намного ниже по сравнению с компиляторами, т.к. при использовании оператора в цикле он транслируется многократно.
Байт-код — это промежуточный подход, при котором программа преобразуется в промежуточный двоичный вид, интерпретируемый некой «виртуальной машиной» во время исполнения.
С начала 70-х годов по настоящее время тянется период языков четвертого поколения (4GL). Несмотря на рождение новых технологий (ООП, визуальное программирование, CASE-методологии, системный анализ), процесс создания больших программных комплексов оказался очень трудоемкой задачей, так как для реализации крупных проектов требовался более глобальный подход к решаемым задачам, чем предлагали имевшиеся средства разработки. Языки 4GL частично снимали эту проблему. Целью их создания было в первую очередь стремление к увеличению скорости разработки проектов, снижение числа ошибок и повышение общей надежности работы больших программных комплексов, возможность быстрого и легкого внесения изменений в готовые проекты, упрощение самих языков для конечного пользователя, активное внедрение технологий визуальной разработки и т. д. Все средства разработки 4-го поколения имеют мощные интегрированные оболочки и обладают простым и удобным пользовательским интерфейсом. Они чаще всего используются для проектирования БД и работы с ними (встроенные языки СУБД), что объясняется возможностью формализации всех понятий, используемых при построении реляционных БД.
Языки 4GL активно применяются в различных специализированных областях, где высоких результатов можно добиться, используя не универсальные, а проблемно-ориентированные языки, оперирующие конкретными понятиями узкой предметной области. Как правило, в эти языки встраиваются мощные примитивы, позволяющие одним оператором описать такую функциональность, для реализации которой на языках младших поколений потребовалось бы написать тысячи строк кода. Однако пользователям, использующим языки 4GL для создания законченных приложений, по-прежнему необходимо кодировать программу вручную, используя обычный процесс последовательного ввода команд. При этом сохраняется главный недостаток языков предыдущих поколений. Он заключается в том, что все они в значительной степени ориентированы на чуждую человеческому мышлению чисто компьютерную идеологию (работа с памятью, переменными, базами данных, последовательностями абстрактных операторов), что требует от людей хорошего понимания принципов функционирования компьютера и ОС.